diff --git a/ChangeLog b/ChangeLog index dcce40e38..a6ff18dd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2002-01-27 Havoc Pennington + + * src/display.c (meta_set_syncing): move in here so util.c doesn't + require display.[hc] + + * src/theme.h, src/theme.c: implement coordinate expression + parser, write MetaShapeSpec declaration + + * src/util.c (meta_exit): move in here so we can link + to util.c with a different main() + + * src/theme.h: rename the MetaWindow* enums to MetaFrame* + 2002-01-27 Peteris Krisjanis * configure.in - Added lv to ALL_LINGUAS diff --git a/src/Makefile.am b/src/Makefile.am index 81f254349..8391041a2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,9 +55,19 @@ metacity_SOURCES= \ xprops.c \ xprops.h -bin_PROGRAMS=metacity +metacity_theme_viewer_SOURCES= \ + gradient.c \ + gradient.h \ + theme.c \ + theme.h \ + theme-viewer.c \ + util.c \ + util.h + +bin_PROGRAMS=metacity metacity-theme-viewer metacity_LDADD= @METACITY_LIBS@ +metacity_theme_viewer_LDADD= @METACITY_LIBS@ testgradient_SOURCES=gradient.h gradient.c testgradient.c diff --git a/src/display.c b/src/display.c index 8528f8e8f..45511d549 100644 --- a/src/display.c +++ b/src/display.c @@ -2057,3 +2057,30 @@ meta_display_unshow_desktop (MetaDisplay *display) queue_windows_showing (display); } + +static gboolean is_syncing = FALSE; + +gboolean +meta_is_syncing (void) +{ + return is_syncing; +} + +void +meta_set_syncing (gboolean setting) +{ + if (setting != is_syncing) + { + GSList *tmp; + + is_syncing = setting; + + tmp = meta_displays_list (); + while (tmp != NULL) + { + MetaDisplay *display = tmp->data; + XSynchronize (display->xdisplay, is_syncing); + tmp = tmp->next; + } + } +} diff --git a/src/main.c b/src/main.c index 2228bb78f..87d2d8c8c 100644 --- a/src/main.c +++ b/src/main.c @@ -274,13 +274,6 @@ meta_quit (MetaExitCode code) g_main_quit (meta_main_loop); } -void -meta_exit (MetaExitCode code) -{ - - exit (code); -} - void meta_restart (void) { diff --git a/src/theme-viewer.c b/src/theme-viewer.c new file mode 100644 index 000000000..59af1fdcb --- /dev/null +++ b/src/theme-viewer.c @@ -0,0 +1,247 @@ +/* Metacity theme viewer and test app main() */ + +/* + * Copyright (C) 2002 Havoc Pennington + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include "util.h" +#include "theme.h" +#include + +static void run_position_expression_tests (void); + +int +main (int argc, char **argv) +{ + bindtextdomain (GETTEXT_PACKAGE, METACITY_LOCALEDIR); + + run_position_expression_tests (); + + return 0; +} + +typedef struct +{ + GdkRectangle rect; + const char *expr; + int expected_x; + int expected_y; + MetaPositionExprError expected_error; +} PositionExpressionTest; + +#define NO_ERROR -1 + +static const PositionExpressionTest position_expression_tests[] = { + /* Just numbers */ + { { 10, 20, 40, 50 }, + "10", 20, 30, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14.37", 24, 34, NO_ERROR }, + /* Binary expressions with 2 ints */ + { { 10, 20, 40, 50 }, + "14 * 10", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14 + 10", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14 - 10", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 / 2", 14, 24, NO_ERROR }, + /* Binary expressions with floats and mixed float/ints */ + { { 10, 20, 40, 50 }, + "7.0 / 3.5", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12.1 / 3", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12 / 2.95", 14, 24, NO_ERROR }, + /* Binary expressions without whitespace after first number */ + { { 10, 20, 40, 50 }, + "14* 10", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14+ 10", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14- 10", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8/ 2", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "7.0/ 3.5", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12.1/ 3", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12/ 2.95", 14, 24, NO_ERROR }, + /* Binary expressions without whitespace before second number */ + { { 10, 20, 40, 50 }, + "14 *10", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14 +10", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14 -10", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 /2", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "7.0 /3.5", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12.1 /3", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12 /2.95", 14, 24, NO_ERROR }, + /* Binary expressions without any whitespace */ + { { 10, 20, 40, 50 }, + "14*10", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14+10", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14-10", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8/2", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "7.0/3.5", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12.1/3", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "12/2.95", 14, 24, NO_ERROR }, + /* Binary expressions with parentheses */ + { { 10, 20, 40, 50 }, + "(14) * (10)", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(14) + (10)", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(14) - (10)", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(8) / (2)", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(7.0) / (3.5)", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(12.1) / (3)", 14, 24, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(12) / (2.95)", 14, 24, NO_ERROR }, + /* Lots of extra parentheses */ + { { 10, 20, 40, 50 }, + "(((14)) * ((10)))", 150, 160, NO_ERROR }, + { { 10, 20, 40, 50 }, + "((((14)))) + ((((((((10))))))))", 34, 44, NO_ERROR }, + { { 10, 20, 40, 50 }, + "((((((((((14 - 10))))))))))", 14, 24, NO_ERROR }, + /* Binary expressions with variables */ + { { 10, 20, 40, 50 }, + "2 * width", 90, 100, NO_ERROR }, + { { 10, 20, 40, 50 }, + "2 * height", 110, 120, NO_ERROR }, + { { 10, 20, 40, 50 }, + "width - 10", 40, 50, NO_ERROR }, + { { 10, 20, 40, 50 }, + "height / 2", 35, 45, NO_ERROR }, + /* More than two operands */ + { { 10, 20, 40, 50 }, + "8 / 2 + 5", 19, 29, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 * 2 + 5", 31, 41, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 + 2 * 5", 28, 38, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 + 8 / 2", 22, 32, NO_ERROR }, + { { 10, 20, 40, 50 }, + "14 / (2 + 5)", 12, 22, NO_ERROR }, + { { 10, 20, 40, 50 }, + "8 * (2 + 5)", 66, 76, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(8 + 2) * 5", 60, 70, NO_ERROR }, + { { 10, 20, 40, 50 }, + "(8 + 8) / 2", 18, 28, NO_ERROR }, + /* Errors */ + { { 10, 20, 40, 50 }, + "2 * foo", 0, 0, META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE }, + { { 10, 20, 40, 50 }, + "2 *", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "- width", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "! * 2", 0, 0, META_POSITION_EXPR_ERROR_BAD_CHARACTER }, + { { 10, 20, 40, 50 }, + " ", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "() () (( ) ()) ((()))", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "(*) () ((/) ()) ((()))", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "2 * 5 /", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "+ 2 * 5", 0, 0, META_POSITION_EXPR_ERROR_FAILED }, + { { 10, 20, 40, 50 }, + "+ 2 * 5", 0, 0, META_POSITION_EXPR_ERROR_FAILED } +}; + +static void +run_position_expression_tests (void) +{ + int i; + + i = 0; + while (i < G_N_ELEMENTS (position_expression_tests)) + { + GError *err; + gboolean retval; + const PositionExpressionTest *test; + int x, y; + + test = &position_expression_tests[i]; + + if (g_getenv ("META_PRINT_TESTS") != NULL) + g_print ("Test expression: \"%s\" expecting x = %d y = %d", + test->expr, test->expected_x, test->expected_y); + + err = NULL; + + retval = meta_parse_position_expression (test->expr, + test->rect.x, + test->rect.y, + test->rect.width, + test->rect.height, + &x, &y, + &err); + + if (retval && err) + g_error ("position expression test returned TRUE but set error"); + if (!retval && err == NULL) + g_error ("position expression test returned FALSE but didn't set error"); + if (test->expected_error != NO_ERROR) + { + if (err == NULL) + g_error ("Error was expected but none given"); + if (err->code != test->expected_error) + g_error ("Error %d was expected but %d given", + test->expected_error, err->code); + } + else + { + if (err) + g_error ("Error not expected but one was returned: %s", + err->message); + + if (x != test->expected_x) + g_error ("x value was %d, %d was expected", x, test->expected_x); + + if (y != test->expected_y) + g_error ("y value was %d, %d was expected", y, test->expected_y); + } + + if (err) + g_error_free (err); + + ++i; + } +} diff --git a/src/theme.c b/src/theme.c index cade72e02..65e9cf209 100644 --- a/src/theme.c +++ b/src/theme.c @@ -19,11 +19,13 @@ * 02111-1307, USA. */ +#include #include "theme.h" #include "util.h" #include "gradient.h" #include #include +#include #define GDK_COLOR_RGBA(color) \ (0xff | \ @@ -293,6 +295,975 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, } } +typedef enum +{ + POS_TOKEN_INT, + POS_TOKEN_DOUBLE, + POS_TOKEN_OPERATOR, + POS_TOKEN_VARIABLE, + POS_TOKEN_OPEN_PAREN, + POS_TOKEN_CLOSE_PAREN +} PosTokenType; + +typedef struct +{ + PosTokenType type; + + union + { + struct { + int val; + } i; + + struct { + double val; + } d; + + struct { + char op; + } o; + + struct { + char *name; + } v; + + } d; + +} PosToken; + + +static void +free_tokens (PosToken *tokens, + int n_tokens) +{ + int i; + + /* n_tokens can be 0 since tokens may have been allocated more than + * it was initialized + */ + + i = 0; + while (i < n_tokens) + { + if (tokens[i].type == POS_TOKEN_VARIABLE) + g_free (tokens[i].d.v.name); + ++i; + } + g_free (tokens); +} + +static gboolean +parse_number (const char *p, + const char **end_return, + PosToken *next, + GError **err) +{ + const char *start = p; + char *end; + gboolean is_float; + char *num_str; + + while (*p && (*p == '.' || g_ascii_isdigit (*p))) + ++p; + + if (p == start) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_BAD_CHARACTER, + _("Coordinate expression contains character '%c' which is not allowed"), + *p); + return FALSE; + } + + *end_return = p; + + /* we need this to exclude floats like "1e6" */ + num_str = g_strndup (start, p - start); + start = num_str; + is_float = FALSE; + while (*start) + { + if (*start == '.') + is_float = TRUE; + ++start; + } + + if (is_float) + { + next->type = POS_TOKEN_DOUBLE; + next->d.d.val = g_ascii_strtod (num_str, &end); + + if (end == num_str) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression contains floating point number '%s' which could not be parsed"), + num_str); + g_free (num_str); + return FALSE; + } + } + else + { + next->type = POS_TOKEN_INT; + next->d.i.val = strtol (num_str, &end, 10); + if (end == num_str) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression contains integer '%s' which could not be parsed"), + num_str); + g_free (num_str); + return FALSE; + } + } + + g_free (num_str); + + g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE); + + return TRUE; +} + +static gboolean +pos_tokenize (const char *expr, + PosToken **tokens_p, + int *n_tokens_p, + GError **err) +{ + PosToken *tokens; + int n_tokens; + int allocated; + const char *p; + + *tokens_p = NULL; + *n_tokens_p = 0; + + allocated = 3; + n_tokens = 0; + tokens = g_new (PosToken, allocated); + + p = expr; + while (*p) + { + PosToken *next; + + if (n_tokens == allocated) + { + allocated *= 2; + tokens = g_renew (PosToken, tokens, allocated); + } + + next = &tokens[n_tokens]; + + switch (*p) + { + case '*': + case '/': + case '+': + case '-': /* negative numbers aren't allowed so this is easy */ + next->type = POS_TOKEN_OPERATOR; + next->d.o.op = *p; + ++n_tokens; + break; + + case '(': + next->type = POS_TOKEN_OPEN_PAREN; + ++n_tokens; + break; + + case ')': + next->type = POS_TOKEN_CLOSE_PAREN; + ++n_tokens; + break; + + case ' ': + case '\t': + break; + + default: + if (g_ascii_isalpha (*p)) + { + /* Assume variable */ + const char *start = p; + while (*p && g_ascii_isalpha (*p)) + ++p; + g_assert (p != start); + next->type = POS_TOKEN_VARIABLE; + next->d.v.name = g_strndup (start, p - start); + ++n_tokens; + --p; /* since we ++p again at the end of while loop */ + } + else + { + /* Assume number */ + const char *end; + + if (!parse_number (p, &end, next, err)) + goto error; + + ++n_tokens; + p = end - 1; /* -1 since we ++p again at the end of while loop */ + } + + break; + } + + ++p; + } + + if (n_tokens == 0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression was empty or not understood")); + + goto error; + } + + *tokens_p = tokens; + *n_tokens_p = n_tokens; + + return TRUE; + + error: + g_assert (err == NULL || *err != NULL); + + free_tokens (tokens, n_tokens); + return FALSE; +} + +static void +debug_print_tokens (PosToken *tokens, + int n_tokens) +{ + int i; + + i = 0; + while (i < n_tokens) + { + PosToken *t = &tokens[i]; + + g_print (" "); + + switch (t->type) + { + case POS_TOKEN_INT: + g_print ("\"%d\"", t->d.i.val); + break; + case POS_TOKEN_DOUBLE: + g_print ("\"%g\"", t->d.d.val); + break; + case POS_TOKEN_OPEN_PAREN: + g_print ("\"(\""); + break; + case POS_TOKEN_CLOSE_PAREN: + g_print ("\")\""); + break; + case POS_TOKEN_VARIABLE: + g_print ("\"%s\"", t->d.v.name); + break; + case POS_TOKEN_OPERATOR: + g_print ("\"%c\"", t->d.o.op); + break; + } + + ++i; + } + + g_print ("\n"); +} + +typedef enum +{ + POS_EXPR_INT, + POS_EXPR_DOUBLE, + POS_EXPR_OPERATOR +} PosExprType; + +typedef struct +{ + PosExprType type; + union + { + double double_val; + int int_val; + char operator; + } d; +} PosExpr; + +static void +debug_print_exprs (PosExpr *exprs, + int n_exprs) +{ + int i; + + i = 0; + while (i < n_exprs) + { + switch (exprs[i].type) + { + case POS_EXPR_INT: + g_print (" %d", exprs[i].d.int_val); + break; + case POS_EXPR_DOUBLE: + g_print (" %g", exprs[i].d.double_val); + break; + case POS_EXPR_OPERATOR: + g_print (" %c", exprs[i].d.operator); + break; + } + + ++i; + } + g_print ("\n"); +} + +static gboolean +do_operation (PosExpr *a, + PosExpr *b, + char op, + GError **err) +{ + /* Promote types to double if required */ + if (a->type == POS_EXPR_DOUBLE || + b->type == POS_EXPR_DOUBLE) + { + if (a->type != POS_EXPR_DOUBLE) + { + a->type = POS_EXPR_DOUBLE; + a->d.double_val = a->d.int_val; + } + if (b->type != POS_EXPR_DOUBLE) + { + b->type = POS_EXPR_DOUBLE; + b->d.double_val = b->d.int_val; + } + } + + g_assert (a->type == b->type); + + if (a->type == POS_EXPR_INT) + { + switch (op) + { + case '*': + a->d.int_val = a->d.int_val * b->d.int_val; + break; + case '/': + if (b->d.int_val == 0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO, + _("Coordinate expression results in division by zero")); + return FALSE; + } + a->d.int_val = a->d.int_val / b->d.int_val; + break; + case '%': + if (b->d.int_val == 0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO, + _("Coordinate expression results in division by zero")); + return FALSE; + } + a->d.int_val = a->d.int_val % b->d.int_val; + break; + case '+': + a->d.int_val = a->d.int_val + b->d.int_val; + break; + case '-': + a->d.int_val = a->d.int_val - b->d.int_val; + break; + } + } + else if (a->type == POS_EXPR_DOUBLE) + { + switch (op) + { + case '*': + a->d.double_val = a->d.double_val * b->d.double_val; + break; + case '/': + if (b->d.double_val == 0.0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO, + _("Coordinate expression results in division by zero")); + return FALSE; + } + a->d.double_val = a->d.double_val / b->d.double_val; + break; + case '%': + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_MOD_ON_FLOAT, + _("Coordinate expression tries to use mod operator on a floating-point number")); + return FALSE; + break; + case '+': + a->d.double_val = a->d.double_val + b->d.double_val; + break; + case '-': + a->d.double_val = a->d.double_val - b->d.double_val; + break; + } + } + else + g_assert_not_reached (); + + return TRUE; +} + +static gboolean +do_operations (PosExpr *exprs, + int *n_exprs, + int precedence, + GError **err) +{ + int i; + +#if 0 + g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs); + debug_print_exprs (exprs, *n_exprs); +#endif + + i = 1; + while (i < *n_exprs) + { + gboolean compress; + + /* exprs[i-1] first operand + * exprs[i] operator + * exprs[i+1] second operand + * + * we replace first operand with result of mul/div/mod, + * or skip over operator and second operand if we have + * an add/subtract + */ + + if (exprs[i-1].type == POS_EXPR_OPERATOR) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression has an operator \"%c\" where an operand was expected"), + exprs[i-1].d.operator); + return FALSE; + } + + if (exprs[i].type != POS_EXPR_OPERATOR) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression had an operand where an operator was expected")); + return FALSE; + } + + if (i == (*n_exprs - 1)) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression ended with an operator instead of an operand")); + return FALSE; + } + + g_assert ((i+1) < *n_exprs); + + if (exprs[i+1].type == POS_EXPR_OPERATOR) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"), + exprs[i+1].d.operator, + exprs[i].d.operator); + return FALSE; + } + + compress = FALSE; + + if (precedence == 1) + { + switch (exprs[i].d.operator) + { + case '*': + case '/': + case '%': + compress = TRUE; + if (!do_operation (&exprs[i-1], &exprs[i+1], + exprs[i].d.operator, + err)) + return FALSE; + break; + } + } + else if (precedence == 0) + { + switch (exprs[i].d.operator) + { + case '-': + case '+': + compress = TRUE; + if (!do_operation (&exprs[i-1], &exprs[i+1], + exprs[i].d.operator, + err)) + return FALSE; + break; + } + } + + if (compress) + { + /* exprs[i-1] first operand (now result) + * exprs[i] operator + * exprs[i+1] second operand + * exprs[i+2] new operator + * + * we move new operator just after first operand + */ + if ((i+2) < *n_exprs) + { + g_memmove (&exprs[i], &exprs[i+2], + sizeof (PosExpr) * (*n_exprs - i - 2)); + } + + *n_exprs -= 2; + } + else + { + /* Skip operator and next operand */ + i += 2; + } + } + + return TRUE; +} + +static gboolean +pos_eval_helper (PosToken *tokens, + int n_tokens, + int width, + int height, + PosExpr *result, + GError **err) +{ + /* lazy-ass hardcoded limit on expression size */ +#define MAX_EXPRS 32 + int paren_level; + int first_paren; + int i; + PosExpr exprs[MAX_EXPRS]; + int n_exprs; + + g_print ("Pos eval helper on %d tokens:\n", n_tokens); + debug_print_tokens (tokens, n_tokens); + + /* Our first goal is to get a list of PosExpr, essentially + * substituting variables and handling parentheses. + */ + + first_paren = 0; + paren_level = 0; + n_exprs = 0; + i = 0; + while (i < n_tokens) + { + PosToken *t = &tokens[i]; + + if (n_exprs >= MAX_EXPRS) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression parser overflowed its buffer, this is really a Metacity bug, but are you sure you need a huge expression like that?")); + return FALSE; + } + + if (paren_level == 0) + { + switch (t->type) + { + case POS_TOKEN_INT: + exprs[n_exprs].type = POS_EXPR_INT; + exprs[n_exprs].d.int_val = t->d.i.val; + ++n_exprs; + break; + + case POS_TOKEN_DOUBLE: + exprs[n_exprs].type = POS_EXPR_DOUBLE; + exprs[n_exprs].d.double_val = t->d.d.val; + ++n_exprs; + break; + + case POS_TOKEN_OPEN_PAREN: + ++paren_level; + if (paren_level == 1) + first_paren = i; + break; + + case POS_TOKEN_CLOSE_PAREN: + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_BAD_PARENS, + _("Coordinate expression had a close parenthesis with no open parenthesis")); + return FALSE; + break; + + case POS_TOKEN_VARIABLE: + exprs[n_exprs].type = POS_EXPR_INT; + + if (strcmp (t->d.v.name, "width") == 0) + exprs[n_exprs].d.int_val = width; + else if (strcmp (t->d.v.name, "height") == 0) + exprs[n_exprs].d.int_val = height; + else + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE, + _("Coordinate expression had unknown variable \"%s\""), + t->d.v.name); + return FALSE; + } + ++n_exprs; + break; + + case POS_TOKEN_OPERATOR: + exprs[n_exprs].type = POS_EXPR_OPERATOR; + exprs[n_exprs].d.operator = t->d.o.op; + ++n_exprs; + break; + } + } + else + { + g_assert (paren_level > 0); + + switch (t->type) + { + case POS_TOKEN_INT: + case POS_TOKEN_DOUBLE: + case POS_TOKEN_VARIABLE: + case POS_TOKEN_OPERATOR: + break; + + case POS_TOKEN_OPEN_PAREN: + ++paren_level; + break; + + case POS_TOKEN_CLOSE_PAREN: + if (paren_level == 1) + { + /* We closed a toplevel paren group, so recurse */ + if (!pos_eval_helper (&tokens[first_paren+1], + i - first_paren - 1, + width, height, + &exprs[n_exprs], + err)) + return FALSE; + + ++n_exprs; + } + + --paren_level; + break; + + } + } + + ++i; + } + + if (paren_level > 0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_BAD_PARENS, + _("Coordinate expression had an open parenthesis with no close parenthesis")); + return FALSE; + } + + /* Now we have no parens and no vars; so we just do all the multiplies + * and divides, then all the add and subtract. + */ + if (n_exprs == 0) + { + g_set_error (err, META_POSITION_EXPR_ERROR, + META_POSITION_EXPR_ERROR_FAILED, + _("Coordinate expression doesn't seem to have any operators or operands")); + return FALSE; + } + + /* precedence 1 ops */ + if (!do_operations (exprs, &n_exprs, 1, err)) + return FALSE; + + /* precedence 0 ops */ + if (!do_operations (exprs, &n_exprs, 0, err)) + return FALSE; + + g_assert (n_exprs == 1); + + *result = *exprs; + + return TRUE; +} + +/* + * expr = int | double | expr * expr | expr / expr | + * expr + expr | expr - expr | (expr) + * + * so very not worth fooling with bison, yet so very painful by hand. + */ +static gboolean +pos_eval (PosToken *tokens, + int n_tokens, + int width, + int height, + int *val_p, + GError **err) +{ + PosExpr expr; + + *val_p = 0; + + if (pos_eval_helper (tokens, n_tokens, width, height, &expr, err)) + { + switch (expr.type) + { + case POS_EXPR_INT: + *val_p = expr.d.int_val; + break; + case POS_EXPR_DOUBLE: + *val_p = expr.d.double_val; + break; + case POS_EXPR_OPERATOR: + g_assert_not_reached (); + break; + } + return TRUE; + } + else + { + return FALSE; + } +} + +/* We always return both X and Y, but only one will be meaningful in + * most contexts. + */ +#define FILL_RETURN(number) do { \ + if (x_return) \ + *x_return = x + (number); \ + if (y_return) \ + *y_return = y + (number); \ + } while (0) + +gboolean +meta_parse_position_expression (const char *expr, + int x, int y, int width, int height, + int *x_return, int *y_return, + GError **err) +{ + /* All positions are in a coordinate system with x, y at the origin. + * The expression can have -, +, *, / as operators, floating + * point or integer constants, and the two variables "width" + * and "height". Negative numbers aren't allowed. + */ + PosToken *tokens; + int n_tokens; + int val; + + if (!pos_tokenize (expr, &tokens, &n_tokens, err)) + { + g_assert (err == NULL || *err != NULL); + return FALSE; + } + +#if 0 + g_print ("Tokenized \"%s\" to --->\n", expr); + debug_print_tokens (tokens, n_tokens); +#endif + + /* Optimization for "just a number" */ + if (n_tokens == 1) + { + switch (tokens[0].type) + { + case POS_TOKEN_INT: + FILL_RETURN (tokens[0].d.i.val); + free_tokens (tokens, n_tokens); + return TRUE; + break; + + case POS_TOKEN_DOUBLE: + FILL_RETURN (tokens[0].d.d.val); + free_tokens (tokens, n_tokens); + return TRUE; + break; + + default: + /* handle normally */ + break; + } + } + + if (pos_eval (tokens, n_tokens, width, height, &val, err)) + { + FILL_RETURN (val); + free_tokens (tokens, n_tokens); + return TRUE; + } + else + { + g_assert (err == NULL || *err != NULL); + free_tokens (tokens, n_tokens); + return FALSE; + } +} + +MetaShapeSpec* +meta_shape_spec_new (MetaShapeType type) +{ + MetaShapeSpec *spec; + MetaShapeSpec dummy; + int size; + + size = G_STRUCT_OFFSET (MetaShapeSpec, data); + + switch (type) + { + case META_SHAPE_LINE: + size += sizeof (dummy.data.line); + break; + + case META_SHAPE_RECTANGLE: + size += sizeof (dummy.data.rectangle); + break; + + case META_SHAPE_ARC: + size += sizeof (dummy.data.arc); + break; + + case META_SHAPE_TEXTURE: + size += sizeof (dummy.data.texture); + break; + + case META_SHAPE_GTK_ARROW: + size += sizeof (dummy.data.gtk_arrow); + break; + + case META_SHAPE_GTK_BOX: + size += sizeof (dummy.data.gtk_box); + break; + + case META_SHAPE_GTK_VLINE: + size += sizeof (dummy.data.gtk_vline); + break; + } + + spec = g_malloc0 (size); + + spec->type = type; + + return spec; +} + +void +meta_shape_spec_free (MetaShapeSpec *spec) +{ + g_return_if_fail (spec != NULL); + + switch (spec->type) + { + case META_SHAPE_LINE: + g_free (spec->data.line.x1); + g_free (spec->data.line.y1); + g_free (spec->data.line.x2); + g_free (spec->data.line.y2); + break; + + case META_SHAPE_RECTANGLE: + if (spec->data.rectangle.color_spec) + g_free (spec->data.rectangle.color_spec); + g_free (spec->data.rectangle.x); + g_free (spec->data.rectangle.y); + g_free (spec->data.rectangle.width); + g_free (spec->data.rectangle.height); + break; + + case META_SHAPE_ARC: + if (spec->data.arc.color_spec) + g_free (spec->data.arc.color_spec); + g_free (spec->data.arc.x); + g_free (spec->data.arc.y); + g_free (spec->data.arc.width); + g_free (spec->data.arc.height); + break; + + case META_SHAPE_TEXTURE: + if (spec->data.texture.texture_spec) + meta_texture_spec_free (spec->data.texture.texture_spec); + g_free (spec->data.texture.x); + g_free (spec->data.texture.y); + g_free (spec->data.texture.width); + g_free (spec->data.texture.height); + break; + + case META_SHAPE_GTK_ARROW: + g_free (spec->data.gtk_arrow.x); + g_free (spec->data.gtk_arrow.y); + g_free (spec->data.gtk_arrow.width); + g_free (spec->data.gtk_arrow.height); + break; + + case META_SHAPE_GTK_BOX: + g_free (spec->data.gtk_box.x); + g_free (spec->data.gtk_box.y); + g_free (spec->data.gtk_box.width); + g_free (spec->data.gtk_box.height); + break; + + case META_SHAPE_GTK_VLINE: + g_free (spec->data.gtk_vline.x); + g_free (spec->data.gtk_vline.y1); + g_free (spec->data.gtk_vline.y2); + break; + } + + g_free (spec); +} + +void +meta_shape_spec_draw (const MetaShapeSpec *spec, + GtkWidget *widget, + GdkDrawable *drawable, + const GdkRectangle *clip, + int x, + int y, + int width, + int height) +{ + switch (spec->type) + { + case META_SHAPE_LINE: + break; + + case META_SHAPE_RECTANGLE: + break; + + case META_SHAPE_ARC: + break; + + case META_SHAPE_TEXTURE: + break; + + case META_SHAPE_GTK_ARROW: + break; + + case META_SHAPE_GTK_BOX: + break; + + case META_SHAPE_GTK_VLINE: + break; + } +} + MetaGradientSpec* meta_gradient_spec_new (MetaGradientType type) { @@ -1186,13 +2157,17 @@ meta_texture_spec_draw (const MetaTextureSpec *spec, } MetaFrameStyle* -meta_frame_style_new (void) +meta_frame_style_new (MetaFrameStyle *parent) { MetaFrameStyle *style; style = g_new0 (MetaFrameStyle, 1); style->refcount = 1; + + style->parent = parent; + if (parent) + meta_frame_style_ref (parent); return style; } @@ -1252,28 +2227,36 @@ meta_frame_style_unref (MetaFrameStyle *style) if (style->layout) meta_frame_layout_free (style->layout); + + /* we hold a reference to any parent style */ + if (style->parent) + meta_frame_style_unref (style->parent); g_free (style); } } MetaFrameStyleSet* -meta_frame_style_set_new (void) +meta_frame_style_set_new (MetaFrameStyleSet *parent) { MetaFrameStyleSet *style_set; style_set = g_new0 (MetaFrameStyleSet, 1); + style_set->parent = parent; + if (parent) + meta_frame_style_set_ref (parent); + return style_set; } static void -free_focus_styles (MetaFrameStyle *focus_styles[META_WINDOW_FOCUS_LAST]) +free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST]) { int i; i = 0; - while (i < META_WINDOW_FOCUS_LAST) + while (i < META_FRAME_FOCUS_LAST) { if (focus_styles[i]) meta_frame_style_unref (focus_styles[i]); @@ -1283,21 +2266,85 @@ free_focus_styles (MetaFrameStyle *focus_styles[META_WINDOW_FOCUS_LAST]) } void -meta_frame_style_set_free (MetaFrameStyleSet *style_set) +meta_frame_style_set_ref (MetaFrameStyleSet *style_set) +{ + g_return_if_fail (style_set != NULL); + + style_set->refcount += 1; +} + +void +meta_frame_style_set_unref (MetaFrameStyleSet *style_set) +{ + g_return_if_fail (style_set != NULL); + g_return_if_fail (style_set->refcount > 0); + + style_set->refcount -= 1; + + if (style_set->refcount == 0) + { + int i; + + i = 0; + while (i < META_FRAME_RESIZE_LAST) + { + free_focus_styles (style_set->normal_styles[i]); + + ++i; + } + + free_focus_styles (style_set->maximized_styles); + free_focus_styles (style_set->shaded_styles); + free_focus_styles (style_set->maximized_and_shaded_styles); + + if (style_set->parent) + meta_frame_style_set_unref (style_set->parent); + + g_free (style_set); + } +} + +MetaTheme* +meta_theme_new (void) +{ + MetaTheme *theme; + + theme = g_new0 (MetaTheme, 1); + + theme->styles_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) meta_frame_style_unref); + + theme->style_sets_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) meta_frame_style_set_unref); + + + return theme; +} + +void +meta_theme_free (MetaTheme *theme) { int i; + + g_return_if_fail (theme != NULL); + + g_free (theme->name); + g_free (theme->filename); + + g_hash_table_destroy (theme->styles_by_name); + g_hash_table_destroy (theme->style_sets_by_name); i = 0; - while (i < META_WINDOW_RESIZE_LAST) + while (i < META_FRAME_TYPE_LAST) { - free_focus_styles (style_set->normal_styles[i]); - + if (theme->style_sets_by_type[i]) + meta_frame_style_set_unref (theme->style_sets_by_type[i]); ++i; } - - free_focus_styles (style_set->maximized_styles); - free_focus_styles (style_set->shaded_styles); - free_focus_styles (style_set->maximized_and_shaded_styles); - g_free (style_set); + g_free (theme); } diff --git a/src/theme.h b/src/theme.h index f289a0190..4bd258153 100644 --- a/src/theme.h +++ b/src/theme.h @@ -31,10 +31,19 @@ typedef struct _MetaFrameStyleSet MetaFrameStyleSet; typedef struct _MetaTextureSpec MetaTextureSpec; typedef struct _MetaGradientSpec MetaGradientSpec; typedef struct _MetaColorSpec MetaColorSpec; +typedef struct _MetaShapeSpec MetaShapeSpec; typedef struct _MetaFrameLayout MetaFrameLayout; typedef struct _MetaFrameGeometry MetaFrameGeometry; typedef struct _MetaTheme MetaTheme; +typedef enum +{ + META_TEXTURE_DRAW_UNSCALED, + META_TEXTURE_DRAW_SCALED_VERTICALLY, + META_TEXTURE_DRAW_SCALED_HORIZONTALLY, + META_TEXTURE_DRAW_SCALED_BOTH +} MetaTextureDrawMode; + /* Parameters used to calculate the geometry of the frame */ struct _MetaFrameLayout { @@ -121,6 +130,100 @@ struct _MetaColorSpec } data; }; +typedef enum +{ + /* Basic drawing */ + META_SHAPE_LINE, + META_SHAPE_RECTANGLE, + META_SHAPE_ARC, + + /* Texture in a specific rectangle */ + META_SHAPE_TEXTURE, + + /* GTK theme engine stuff */ + META_SHAPE_GTK_ARROW, + META_SHAPE_GTK_BOX, + META_SHAPE_GTK_VLINE +} MetaShapeType; + +struct _MetaShapeSpec +{ + MetaShapeType type; + + /* Positions are strings because they can be expressions */ + union + { + struct { + MetaColorSpec *color_spec; + int dash_on_length; + int dash_off_length; + int width; + char *x1; + char *y1; + char *x2; + char *y2; + } line; + + struct { + MetaColorSpec *color_spec; + gboolean filled; + char *x; + char *y; + char *width; + char *height; + } rectangle; + + struct { + MetaColorSpec *color_spec; + gboolean filled; + char *x; + char *y; + char *width; + char *height; + double start_angle; + double extent_angle; + } arc; + + struct { + MetaTextureSpec *texture_spec; + MetaTextureDrawMode mode; + double xalign; + double yalign; + char *x; + char *y; + char *width; + char *height; + } texture; + + struct { + GtkStateType state; + GtkShadowType shadow; + GtkArrowType arrow; + char *x; + char *y; + char *width; + char *height; + } gtk_arrow; + + struct { + GtkStateType state; + GtkShadowType shadow; + char *x; + char *y; + char *width; + char *height; + } gtk_box; + + struct { + GtkStateType state; + char *x; + char *y1; + char *y2; + } gtk_vline; + + } data; +}; + struct _MetaGradientSpec { MetaGradientType type; @@ -135,14 +238,6 @@ typedef enum META_TEXTURE_COMPOSITE } MetaTextureType; -typedef enum -{ - META_TEXTURE_DRAW_UNSCALED, - META_TEXTURE_DRAW_SCALED_VERTICALLY, - META_TEXTURE_DRAW_SCALED_HORIZONTALLY, - META_TEXTURE_DRAW_SCALED_BOTH -} MetaTextureDrawMode; - struct _MetaTextureSpec { MetaTextureType type; @@ -252,6 +347,8 @@ typedef enum META_FRAME_PIECE_LEFT_END_OF_BOTTOM_EDGE, /* place on right end of bottom edge of the frame, unscaled */ META_FRAME_PIECE_RIGHT_END_OF_BOTTOM_EDGE, + /* place over entire frame, after drawing everything else */ + META_FRAME_PIECE_OVERLAY, /* Used to get size of the enum */ META_FRAME_PIECE_LAST } MetaFramePiece; @@ -259,16 +356,15 @@ typedef enum struct _MetaFrameStyle { int refcount; + MetaFrameStyle *parent; MetaTextureSpec *button_icons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaTextureSpec *button_backgrounds[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaTextureSpec *pieces[META_FRAME_PIECE_LAST]; MetaFrameLayout *layout; }; - -/* FIXME dammit, these are not mutually exclusive; how to handle - * the mess... - * +/* Kinds of frame... + * * normal -> noresize / vert only / horz only / both * focused / unfocused * max -> focused / unfocused @@ -284,43 +380,48 @@ struct _MetaFrameStyle */ typedef enum { - META_WINDOW_STATE_NORMAL, - META_WINDOW_STATE_MAXIMIZED, - META_WINDOW_STATE_SHADED, - META_WINDOW_STATE_MAXIMIZED_AND_SHADED, - META_WINDOW_STATE_LAST -} MetaWindowState; + META_FRAME_STATE_NORMAL, + META_FRAME_STATE_MAXIMIZED, + META_FRAME_STATE_SHADED, + META_FRAME_STATE_MAXIMIZED_AND_SHADED, + META_FRAME_STATE_LAST +} MetaFrameState; typedef enum { - META_WINDOW_RESIZE_NONE, - META_WINDOW_RESIZE_VERTICAL, - META_WINDOW_RESIZE_HORIZONTAL, - META_WINDOW_RESIZE_BOTH, - META_WINDOW_RESIZE_LAST -} MetaWindowResize; + META_FRAME_RESIZE_NONE, + META_FRAME_RESIZE_VERTICAL, + META_FRAME_RESIZE_HORIZONTAL, + META_FRAME_RESIZE_BOTH, + META_FRAME_RESIZE_LAST +} MetaFrameResize; typedef enum { - META_WINDOW_FOCUS_NO, - META_WINDOW_FOCUS_YES, - META_WINDOW_FOCUS_LAST -} MetaWindowFocus; + META_FRAME_FOCUS_NO, + META_FRAME_FOCUS_YES, + META_FRAME_FOCUS_LAST +} MetaFrameFocus; /* One StyleSet per window type (for window types that get a frame) */ struct _MetaFrameStyleSet { - MetaFrameStyle *normal_styles[META_WINDOW_RESIZE_LAST][META_WINDOW_FOCUS_LAST]; - MetaFrameStyle *maximized_styles[META_WINDOW_FOCUS_LAST]; - MetaFrameStyle *shaded_styles[META_WINDOW_FOCUS_LAST]; - MetaFrameStyle *maximized_and_shaded_styles[META_WINDOW_FOCUS_LAST]; + int refcount; + MetaFrameStyleSet *parent; + MetaFrameStyle *normal_styles[META_FRAME_RESIZE_LAST][META_FRAME_FOCUS_LAST]; + MetaFrameStyle *maximized_styles[META_FRAME_FOCUS_LAST]; + MetaFrameStyle *shaded_styles[META_FRAME_FOCUS_LAST]; + MetaFrameStyle *maximized_and_shaded_styles[META_FRAME_FOCUS_LAST]; }; struct _MetaTheme { char *name; char *filename; - MetaFrameStyleSet *style_sets[META_FRAME_TYPE_LAST]; + + GHashTable *styles_by_name; + GHashTable *style_sets_by_name; + MetaFrameStyleSet *style_sets_by_type[META_FRAME_TYPE_LAST]; }; MetaFrameLayout* meta_frame_layout_new (void); @@ -341,6 +442,21 @@ void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, int client_height, MetaFrameGeometry *fgeom); +#define META_POSITION_EXPR_ERROR (g_quark_from_static_string ("meta-position-expr-error")) +typedef enum +{ + META_POSITION_EXPR_ERROR_BAD_CHARACTER, + META_POSITION_EXPR_ERROR_BAD_PARENS, + META_POSITION_EXPR_ERROR_UNKNOWN_VARIABLE, + META_POSITION_EXPR_ERROR_DIVIDE_BY_ZERO, + META_POSITION_EXPR_ERROR_MOD_ON_FLOAT, + META_POSITION_EXPR_ERROR_FAILED +} MetaPositionExprError; + +gboolean meta_parse_position_expression (const char *expr, + int x, int y, int width, int height, + int *x_return, int *y_return, + GError **err); MetaColorSpec* meta_color_spec_new (MetaColorSpecType type); void meta_color_spec_free (MetaColorSpec *spec); @@ -348,6 +464,23 @@ void meta_color_spec_render (MetaColorSpec *spec, GtkWidget *widget, GdkColor *color); +MetaShapeSpec* meta_shape_spec_new (MetaShapeType type); +void meta_shape_spec_free (MetaShapeSpec *spec); +void meta_shape_spec_draw (const MetaShapeSpec *spec, + GtkWidget *widget, + GdkDrawable *drawable, + const GdkRectangle *clip, + /* logical region being drawn, + * shape coords are offset by + * x,y and w/h variables are + * available in shape coord + * expressions. + */ + int x, + int y, + int width, + int height); + MetaGradientSpec* meta_gradient_spec_new (MetaGradientType type); void meta_gradient_spec_free (MetaGradientSpec *desc); GdkPixbuf* meta_gradient_spec_render (const MetaGradientSpec *desc, @@ -376,11 +509,15 @@ void meta_texture_spec_draw (const MetaTextureSpec *desc, int width, int height); -MetaFrameStyle* meta_frame_style_new (void); +MetaFrameStyle* meta_frame_style_new (MetaFrameStyle *parent); void meta_frame_style_ref (MetaFrameStyle *style); void meta_frame_style_unref (MetaFrameStyle *style); -MetaFrameStyleSet* meta_frame_style_set_new (void); -void meta_frame_style_set_free (MetaFrameStyleSet *style_set); +MetaFrameStyleSet* meta_frame_style_set_new (MetaFrameStyleSet *parent); +void meta_frame_style_set_ref (MetaFrameStyleSet *style_set); +void meta_frame_style_set_unref (MetaFrameStyleSet *style_set); + +MetaTheme* meta_theme_new (void); +void meta_theme_free (MetaTheme *theme); #endif diff --git a/src/util.c b/src/util.c index 40673ac09..da8594251 100644 --- a/src/util.c +++ b/src/util.c @@ -21,7 +21,6 @@ #include "util.h" #include "main.h" -#include "display.h" #include #include @@ -29,7 +28,6 @@ static gboolean is_verbose = FALSE; static gboolean is_debugging = FALSE; -static gboolean is_syncing = FALSE; static int no_prefix = 0; static FILE* logfile = NULL; @@ -88,31 +86,6 @@ meta_set_debugging (gboolean setting) is_debugging = setting; } -gboolean -meta_is_syncing (void) -{ - return is_syncing; -} - -void -meta_set_syncing (gboolean setting) -{ - if (setting != is_syncing) - { - GSList *tmp; - - is_syncing = setting; - - tmp = meta_displays_list (); - while (tmp != NULL) - { - MetaDisplay *display = tmp->data; - XSynchronize (display->xdisplay, is_syncing); - tmp = tmp->next; - } - } -} - void meta_debug_spew (const char *format, ...) { @@ -300,3 +273,10 @@ meta_pop_no_msg_prefix (void) --no_prefix; } + +void +meta_exit (MetaExitCode code) +{ + + exit (code); +}