1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
4
* Copyright (C) 2001 Havoc Pennington
5
* Copyright (C) 2005 Elijah Newren
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU General Public License as
9
* published by the Free Software Foundation; either version 2 of the
10
* License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, see <http://www.gnu.org/licenses/>.
23
* @title: Utility functions
24
* @short_description: Miscellaneous utility functions
27
#define _POSIX_C_SOURCE 200112L /* for fdopen() */
30
#include <meta/common.h>
31
#include "util-private.h"
32
#include <meta/main.h>
34
#include <clutter/clutter.h> /* For clutter_threads_add_repaint_func() */
41
#include <X11/Xlib.h> /* must explicitly be included for Solaris; #326746 */
42
#include <X11/Xutil.h> /* Just for the definition of the various gravities */
44
#ifdef WITH_VERBOSE_MODE
46
meta_topic_real_valist (MetaDebugTopic topic,
48
va_list args) G_GNUC_PRINTF(2, 0);
52
meta_later_remove_from_list (guint later_id, GSList **laters_list);
54
static gint verbose_topics = 0;
55
static gboolean is_debugging = FALSE;
56
static gboolean replace_current = FALSE;
57
static int no_prefix = 0;
58
static gboolean is_wayland_compositor = FALSE;
60
#ifdef WITH_VERBOSE_MODE
61
static FILE* logfile = NULL;
66
if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE"))
68
char *filename = NULL;
73
tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX",
77
fd = g_file_open_tmp (tmpl,
85
meta_warning ("Failed to open debug log: %s\n",
91
logfile = fdopen (fd, "w");
95
meta_warning ("Failed to fdopen() log file %s: %s\n",
96
filename, strerror (errno));
101
g_printerr ("Opened log file %s\n", filename);
110
meta_is_verbose (void)
112
return verbose_topics != 0;
116
meta_set_verbose (gboolean setting)
118
#ifndef WITH_VERBOSE_MODE
120
meta_fatal (_("Mutter was compiled without support for verbose mode\n"));
127
meta_add_verbose_topic (META_DEBUG_VERBOSE);
129
meta_remove_verbose_topic (META_DEBUG_VERBOSE);
133
* meta_add_verbose_topic:
134
* @topic: Topic for which logging will be started
136
* Ensure log messages for the given topic @topic
140
meta_add_verbose_topic (MetaDebugTopic topic)
142
if (verbose_topics == META_DEBUG_VERBOSE)
144
if (topic == META_DEBUG_VERBOSE)
145
verbose_topics = META_DEBUG_VERBOSE;
147
verbose_topics |= topic;
151
* meta_remove_verbose_topic:
152
* @topic: Topic for which logging will be stopped
154
* Stop printing log messages for the given topic @topic. Note
155
* that this method does not stack with meta_add_verbose_topic();
156
* i.e. if two calls to meta_add_verbose_topic() for the same
157
* topic are made, one call to meta_remove_verbose_topic() will
161
meta_remove_verbose_topic (MetaDebugTopic topic)
163
if (topic == META_DEBUG_VERBOSE)
166
verbose_topics &= ~topic;
170
meta_is_debugging (void)
176
meta_set_debugging (gboolean setting)
178
#ifdef WITH_VERBOSE_MODE
183
is_debugging = setting;
187
meta_get_replace_current_wm (void)
189
return replace_current;
193
meta_set_replace_current_wm (gboolean setting)
195
replace_current = setting;
199
meta_is_wayland_compositor (void)
201
return is_wayland_compositor;
205
meta_set_is_wayland_compositor (gboolean value)
207
is_wayland_compositor = value;
211
meta_g_utf8_strndup (const gchar *src,
214
const gchar *s = src;
217
s = g_utf8_next_char (s);
221
return g_strndup (src, s - src);
225
utf8_fputs (const char *str,
231
l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL);
234
retval = fputs (str, f); /* just print it anyway, better than nothing */
236
retval = fputs (l, f);
244
* meta_free_gslist_and_elements: (skip)
245
* @list_to_deep_free: list to deep free
249
meta_free_gslist_and_elements (GSList *list_to_deep_free)
251
g_slist_foreach (list_to_deep_free,
252
(void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
254
g_slist_free (list_to_deep_free);
257
#ifdef WITH_VERBOSE_MODE
259
meta_debug_spew_real (const char *format, ...)
265
g_return_if_fail (format != NULL);
270
va_start (args, format);
271
str = g_strdup_vprintf (format, args);
274
out = logfile ? logfile : stderr;
277
utf8_fputs ("Window manager: ", out);
278
utf8_fputs (str, out);
284
#endif /* WITH_VERBOSE_MODE */
286
#ifdef WITH_VERBOSE_MODE
288
meta_verbose_real (const char *format, ...)
292
va_start (args, format);
293
meta_topic_real_valist (META_DEBUG_VERBOSE, format, args);
296
#endif /* WITH_VERBOSE_MODE */
298
#ifdef WITH_VERBOSE_MODE
300
topic_name (MetaDebugTopic topic)
304
case META_DEBUG_FOCUS:
306
case META_DEBUG_WORKAREA:
308
case META_DEBUG_STACK:
310
case META_DEBUG_THEMES:
314
case META_DEBUG_EVENTS:
316
case META_DEBUG_WINDOW_STATE:
317
return "WINDOW_STATE";
318
case META_DEBUG_WINDOW_OPS:
320
case META_DEBUG_PLACEMENT:
322
case META_DEBUG_GEOMETRY:
324
case META_DEBUG_PING:
326
case META_DEBUG_XINERAMA:
328
case META_DEBUG_KEYBINDINGS:
329
return "KEYBINDINGS";
330
case META_DEBUG_SYNC:
332
case META_DEBUG_ERRORS:
334
case META_DEBUG_STARTUP:
336
case META_DEBUG_PREFS:
338
case META_DEBUG_GROUPS:
340
case META_DEBUG_RESIZING:
342
case META_DEBUG_SHAPES:
344
case META_DEBUG_COMPOSITOR:
346
case META_DEBUG_EDGE_RESISTANCE:
347
return "EDGE_RESISTANCE";
348
case META_DEBUG_DBUS:
350
case META_DEBUG_VERBOSE:
357
static int sync_count = 0;
360
meta_topic_real_valist (MetaDebugTopic topic,
367
g_return_if_fail (format != NULL);
369
if (verbose_topics == 0
370
|| (topic == META_DEBUG_VERBOSE && verbose_topics != META_DEBUG_VERBOSE)
371
|| (!(verbose_topics & topic)))
374
str = g_strdup_vprintf (format, args);
376
out = logfile ? logfile : stderr;
379
fprintf (out, "%s: ", topic_name (topic));
381
if (topic == META_DEBUG_SYNC)
384
fprintf (out, "%d: ", sync_count);
387
utf8_fputs (str, out);
395
meta_topic_real (MetaDebugTopic topic,
401
va_start (args, format);
402
meta_topic_real_valist (topic, format, args);
405
#endif /* WITH_VERBOSE_MODE */
408
meta_bug (const char *format, ...)
414
g_return_if_fail (format != NULL);
416
va_start (args, format);
417
str = g_strdup_vprintf (format, args);
420
#ifdef WITH_VERBOSE_MODE
421
out = logfile ? logfile : stderr;
427
utf8_fputs ("Bug in window manager: ", out);
428
utf8_fputs (str, out);
434
/* stop us in a debugger */
439
meta_warning (const char *format, ...)
445
g_return_if_fail (format != NULL);
447
va_start (args, format);
448
str = g_strdup_vprintf (format, args);
451
#ifdef WITH_VERBOSE_MODE
452
out = logfile ? logfile : stderr;
458
utf8_fputs ("Window manager warning: ", out);
459
utf8_fputs (str, out);
467
meta_fatal (const char *format, ...)
473
g_warn_if_fail (format);
475
meta_exit (META_EXIT_ERROR);
477
va_start (args, format);
478
str = g_strdup_vprintf (format, args);
481
#ifdef WITH_VERBOSE_MODE
482
out = logfile ? logfile : stderr;
488
utf8_fputs ("Window manager error: ", out);
489
utf8_fputs (str, out);
495
meta_exit (META_EXIT_ERROR);
499
meta_push_no_msg_prefix (void)
505
meta_pop_no_msg_prefix (void)
507
g_return_if_fail (no_prefix > 0);
513
meta_exit (MetaExitCode code)
520
meta_unsigned_long_equal (gconstpointer v1,
523
return *((const gulong*) v1) == *((const gulong*) v2);
527
meta_unsigned_long_hash (gconstpointer v)
529
gulong val = * (const gulong *) v;
531
/* I'm not sure this works so well. */
532
#if GLIB_SIZEOF_LONG > 4
533
return (guint) (val ^ (val >> 32));
540
meta_gravity_to_string (int gravity)
544
case NorthWestGravity:
545
return "NorthWestGravity";
548
return "NorthGravity";
550
case NorthEastGravity:
551
return "NorthEastGravity";
554
return "WestGravity";
557
return "CenterGravity";
560
return "EastGravity";
562
case SouthWestGravity:
563
return "SouthWestGravity";
566
return "SouthGravity";
568
case SouthEastGravity:
569
return "SouthEastGravity";
572
return "StaticGravity";
575
return "NorthWestGravity";
581
meta_external_binding_name_for_action (guint keybinding_action)
583
return g_strdup_printf ("external-grab-%u", keybinding_action);
586
/* Command line arguments are passed in the locale encoding; in almost
587
* all cases, we'd hope that is UTF-8 and no conversion is necessary.
588
* If it's not UTF-8, then it's possible that the message isn't
589
* representable in the locale encoding.
592
append_argument (GPtrArray *args,
595
char *locale_arg = g_locale_from_utf8 (arg, -1, NULL, NULL, NULL);
597
/* This is cheesy, but it's better to have a few ???'s in the dialog
598
* for an unresponsive application than no dialog at all appear */
600
locale_arg = g_strdup ("???");
602
g_ptr_array_add (args, locale_arg);
606
* meta_show_dialog: (skip)
607
* @type: type of dialog
611
* @ok_text: text for Ok button
612
* @cancel_text: text for Cancel button
613
* @icon_name: icon name
614
* @transient_for: window XID of parent
620
meta_show_dialog (const char *type,
625
const char *cancel_text,
626
const char *icon_name,
627
const int transient_for,
631
GError *error = NULL;
636
args = g_ptr_array_new ();
638
append_argument (args, "zenity");
639
append_argument (args, type);
643
append_argument (args, "--display");
644
append_argument (args, display);
647
append_argument (args, "--class");
648
append_argument (args, "mutter-dialog");
649
append_argument (args, "--title");
650
append_argument (args, "");
651
append_argument (args, "--text");
652
append_argument (args, message);
656
append_argument (args, "--timeout");
657
append_argument (args, timeout);
662
append_argument (args, "--ok-label");
663
append_argument (args, ok_text);
668
append_argument (args, "--cancel-label");
669
append_argument (args, cancel_text);
674
append_argument (args, "--icon-name");
675
append_argument (args, icon_name);
681
append_argument (args, "--column");
682
append_argument (args, tmp->data);
689
append_argument (args, tmp->data);
695
gchar *env = g_strdup_printf("%d", transient_for);
696
setenv ("WINDOWID", env, 1);
699
append_argument (args, "--modal");
702
g_ptr_array_add (args, NULL); /* NULL-terminate */
706
(gchar**) args->pdata,
708
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
715
unsetenv ("WINDOWID");
717
g_ptr_array_free (args, TRUE);
721
meta_warning ("%s\n", error->message);
722
g_error_free (error);
728
/***************************************************************************
729
* Later functions: like idles but integrated with the Clutter repaint loop
730
***************************************************************************/
732
static guint last_later_id = 0;
741
GDestroyNotify notify;
746
static GSList *laters[] = {
747
NULL, /* META_LATER_RESIZE */
748
NULL, /* META_LATER_CALC_SHOWING */
749
NULL, /* META_LATER_CHECK_FULLSCREEN */
750
NULL, /* META_LATER_SYNC_STACK */
751
NULL, /* META_LATER_BEFORE_REDRAW */
752
NULL, /* META_LATER_IDLE */
754
/* This is a dummy timeline used to get the Clutter master clock running */
755
static ClutterTimeline *later_timeline;
756
static guint later_repaint_func = 0;
758
static void ensure_later_repaint_func (void);
761
unref_later (MetaLater *later)
763
if (--later->ref_count == 0)
767
later->notify (later->data);
768
later->notify = NULL;
770
g_slice_free (MetaLater, later);
775
destroy_later (MetaLater *later)
779
g_source_remove (later->source);
787
run_repaint_laters (GSList **laters_list)
793
for (l = *laters_list; l; l = l->next)
795
MetaLater *later = l->data;
796
if (later->source == 0 ||
797
(later->when <= META_LATER_BEFORE_REDRAW && !later->run_once))
800
laters_copy = g_slist_prepend (laters_copy, later);
803
laters_copy = g_slist_reverse (laters_copy);
805
for (l = laters_copy; l; l = l->next)
807
MetaLater *later = l->data;
809
if (!later->func || !later->func (later->data))
810
meta_later_remove_from_list (later->id, laters_list);
814
g_slist_free (laters_copy);
818
run_all_repaint_laters (gpointer data)
822
gboolean keep_timeline_running = FALSE;
824
for (i = 0; i < G_N_ELEMENTS (laters); i++)
826
run_repaint_laters (&laters[i]);
829
for (i = 0; i < G_N_ELEMENTS (laters); i++)
831
for (l = laters[i]; l; l = l->next)
833
MetaLater *later = l->data;
835
if (later->source == 0)
836
keep_timeline_running = TRUE;
840
if (!keep_timeline_running)
841
clutter_timeline_stop (later_timeline);
843
/* Just keep the repaint func around - it's cheap if the lists are empty */
848
ensure_later_repaint_func (void)
851
later_timeline = clutter_timeline_new (G_MAXUINT);
853
if (later_repaint_func == 0)
854
later_repaint_func = clutter_threads_add_repaint_func (run_all_repaint_laters,
857
/* Make sure the repaint function gets run */
858
clutter_timeline_start (later_timeline);
862
call_idle_later (gpointer data)
864
MetaLater *later = data;
866
if (!later->func (later->data))
868
meta_later_remove (later->id);
873
later->run_once = TRUE;
880
* @when: enumeration value determining the phase at which to run the callback
881
* @func: callback to run later
882
* @data: data to pass to the callback
883
* @notify: function to call to destroy @data when it is no longer in use, or %NULL
885
* Sets up a callback to be called at some later time. @when determines the
886
* particular later occasion at which it is called. This is much like g_idle_add(),
887
* except that the functions interact properly with clutter event handling.
888
* If a "later" function is added from a clutter event handler, and is supposed
889
* to be run before the stage is redrawn, it will be run before that redraw
890
* of the stage, not the next one.
892
* Return value: an integer ID (guaranteed to be non-zero) that can be used
893
* to cancel the callback and prevent it from being run.
896
meta_later_add (MetaLaterType when,
899
GDestroyNotify notify)
901
MetaLater *later = g_slice_new0 (MetaLater);
903
later->id = ++last_later_id;
904
later->ref_count = 1;
908
later->notify = notify;
910
laters[when] = g_slist_prepend (laters[when], later);
914
case META_LATER_RESIZE:
915
/* We add this one two ways - as a high-priority idle and as a
916
* repaint func. If we are in a clutter event callback, the repaint
917
* handler will get hit first, and we'll take care of this function
918
* there so it gets called before the stage is redrawn, even if
919
* we haven't gotten back to the main loop. Otherwise, the idle
920
* handler will get hit first and we want to call this function
921
* there so it will happen before GTK+ repaints.
923
later->source = g_idle_add_full (META_PRIORITY_RESIZE, call_idle_later, later, NULL);
924
g_source_set_name_by_id (later->source, "[mutter] call_idle_later");
925
ensure_later_repaint_func ();
927
case META_LATER_CALC_SHOWING:
928
case META_LATER_CHECK_FULLSCREEN:
929
case META_LATER_SYNC_STACK:
930
case META_LATER_BEFORE_REDRAW:
931
ensure_later_repaint_func ();
933
case META_LATER_IDLE:
934
later->source = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, call_idle_later, later, NULL);
935
g_source_set_name_by_id (later->source, "[mutter] call_idle_later");
943
meta_later_remove_from_list (guint later_id, GSList **laters_list)
947
for (l = *laters_list; l; l = l->next)
949
MetaLater *later = l->data;
951
if (later->id == later_id)
953
*laters_list = g_slist_delete_link (*laters_list, l);
954
/* If this was a "repaint func" later, we just let the
955
* repaint func run and get removed
957
destroy_later (later);
967
* @later_id: the integer ID returned from meta_later_add()
969
* Removes a callback added with meta_later_add()
972
meta_later_remove (guint later_id)
976
for (i = 0; i < G_N_ELEMENTS (laters); i++)
978
if (meta_later_remove_from_list (later_id, &laters[i]))
984
meta_get_locale_direction (void)
986
switch (gtk_get_locale_direction ())
988
case GTK_TEXT_DIR_LTR:
989
return META_LOCALE_DIRECTION_LTR;
990
case GTK_TEXT_DIR_RTL:
991
return META_LOCALE_DIRECTION_RTL;
993
g_assert_not_reached ();
998
meta_generate_random_id (GRand *rand,
1004
/* Generate a random string of printable ASCII characters. */
1006
id = g_new0 (char, length + 1);
1007
for (i = 0; i < length; i++)
1008
id[i] = (char) g_rand_int_range (rand, 32, 127);