~elementary-os/elementaryos/os-patch-mutter-bionic

« back to all changes in this revision

Viewing changes to src/core/util.c

  • Committer: RabbitBot
  • Date: 2018-04-11 14:49:36 UTC
  • Revision ID: rabbitbot@elementary.io-20180411144936-hgymqa9d8d1xfpbh
Initial import, version 3.28.0-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
 
 
3
/*
 
4
 * Copyright (C) 2001 Havoc Pennington
 
5
 * Copyright (C) 2005 Elijah Newren
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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/>.
 
19
 */
 
20
 
 
21
/**
 
22
 * SECTION:util
 
23
 * @title: Utility functions
 
24
 * @short_description: Miscellaneous utility functions
 
25
 */
 
26
 
 
27
#define _POSIX_C_SOURCE 200112L /* for fdopen() */
 
28
 
 
29
#include <config.h>
 
30
#include <meta/common.h>
 
31
#include "util-private.h"
 
32
#include <meta/main.h>
 
33
 
 
34
#include <clutter/clutter.h> /* For clutter_threads_add_repaint_func() */
 
35
 
 
36
#include <stdio.h>
 
37
#include <stdlib.h>
 
38
#include <unistd.h>
 
39
#include <errno.h>
 
40
#include <string.h>
 
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 */
 
43
 
 
44
#ifdef WITH_VERBOSE_MODE
 
45
static void
 
46
meta_topic_real_valist (MetaDebugTopic topic,
 
47
                        const char    *format,
 
48
                        va_list        args) G_GNUC_PRINTF(2, 0);
 
49
#endif
 
50
 
 
51
static gboolean
 
52
meta_later_remove_from_list (guint later_id, GSList **laters_list);
 
53
 
 
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;
 
59
 
 
60
#ifdef WITH_VERBOSE_MODE
 
61
static FILE* logfile = NULL;
 
62
 
 
63
static void
 
64
ensure_logfile (void)
 
65
{
 
66
  if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE"))
 
67
    {
 
68
      char *filename = NULL;
 
69
      char *tmpl;
 
70
      int fd;
 
71
      GError *err;
 
72
 
 
73
      tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX",
 
74
                              (int) getpid ());
 
75
 
 
76
      err = NULL;
 
77
      fd = g_file_open_tmp (tmpl,
 
78
                            &filename,
 
79
                            &err);
 
80
 
 
81
      g_free (tmpl);
 
82
 
 
83
      if (err != NULL)
 
84
        {
 
85
          meta_warning ("Failed to open debug log: %s\n",
 
86
                        err->message);
 
87
          g_error_free (err);
 
88
          return;
 
89
        }
 
90
 
 
91
      logfile = fdopen (fd, "w");
 
92
 
 
93
      if (logfile == NULL)
 
94
        {
 
95
          meta_warning ("Failed to fdopen() log file %s: %s\n",
 
96
                        filename, strerror (errno));
 
97
          close (fd);
 
98
        }
 
99
      else
 
100
        {
 
101
          g_printerr ("Opened log file %s\n", filename);
 
102
        }
 
103
 
 
104
      g_free (filename);
 
105
    }
 
106
}
 
107
#endif
 
108
 
 
109
gboolean
 
110
meta_is_verbose (void)
 
111
{
 
112
  return verbose_topics != 0;
 
113
}
 
114
 
 
115
void
 
116
meta_set_verbose (gboolean setting)
 
117
{
 
118
#ifndef WITH_VERBOSE_MODE
 
119
  if (setting)
 
120
    meta_fatal (_("Mutter was compiled without support for verbose mode\n"));
 
121
#else
 
122
  if (setting)
 
123
    ensure_logfile ();
 
124
#endif
 
125
 
 
126
  if (setting)
 
127
    meta_add_verbose_topic (META_DEBUG_VERBOSE);
 
128
  else
 
129
    meta_remove_verbose_topic (META_DEBUG_VERBOSE);
 
130
}
 
131
 
 
132
/**
 
133
 * meta_add_verbose_topic:
 
134
 * @topic: Topic for which logging will be started
 
135
 *
 
136
 * Ensure log messages for the given topic @topic
 
137
 * will be printed.
 
138
 */
 
139
void
 
140
meta_add_verbose_topic (MetaDebugTopic topic)
 
141
{
 
142
  if (verbose_topics == META_DEBUG_VERBOSE)
 
143
    return;
 
144
  if (topic == META_DEBUG_VERBOSE)
 
145
    verbose_topics = META_DEBUG_VERBOSE;
 
146
  else
 
147
    verbose_topics |= topic;
 
148
}
 
149
 
 
150
/**
 
151
 * meta_remove_verbose_topic:
 
152
 * @topic: Topic for which logging will be stopped
 
153
 *
 
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
 
158
 * remove it.
 
159
 */
 
160
void
 
161
meta_remove_verbose_topic (MetaDebugTopic topic)
 
162
{
 
163
  if (topic == META_DEBUG_VERBOSE)
 
164
    verbose_topics = 0;
 
165
  else
 
166
    verbose_topics &= ~topic;
 
167
}
 
168
 
 
169
gboolean
 
170
meta_is_debugging (void)
 
171
{
 
172
  return is_debugging;
 
173
}
 
174
 
 
175
void
 
176
meta_set_debugging (gboolean setting)
 
177
{
 
178
#ifdef WITH_VERBOSE_MODE
 
179
  if (setting)
 
180
    ensure_logfile ();
 
181
#endif
 
182
 
 
183
  is_debugging = setting;
 
184
}
 
185
 
 
186
gboolean
 
187
meta_get_replace_current_wm (void)
 
188
{
 
189
  return replace_current;
 
190
}
 
191
 
 
192
void
 
193
meta_set_replace_current_wm (gboolean setting)
 
194
{
 
195
  replace_current = setting;
 
196
}
 
197
 
 
198
gboolean
 
199
meta_is_wayland_compositor (void)
 
200
{
 
201
  return is_wayland_compositor;
 
202
}
 
203
 
 
204
void
 
205
meta_set_is_wayland_compositor (gboolean value)
 
206
{
 
207
  is_wayland_compositor = value;
 
208
}
 
209
 
 
210
char *
 
211
meta_g_utf8_strndup (const gchar *src,
 
212
                     gsize        n)
 
213
{
 
214
  const gchar *s = src;
 
215
  while (n && *s)
 
216
    {
 
217
      s = g_utf8_next_char (s);
 
218
      n--;
 
219
    }
 
220
 
 
221
  return g_strndup (src, s - src);
 
222
}
 
223
 
 
224
static int
 
225
utf8_fputs (const char *str,
 
226
            FILE       *f)
 
227
{
 
228
  char *l;
 
229
  int retval;
 
230
 
 
231
  l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL);
 
232
 
 
233
  if (l == NULL)
 
234
    retval = fputs (str, f); /* just print it anyway, better than nothing */
 
235
  else
 
236
    retval = fputs (l, f);
 
237
 
 
238
  g_free (l);
 
239
 
 
240
  return retval;
 
241
}
 
242
 
 
243
/**
 
244
 * meta_free_gslist_and_elements: (skip)
 
245
 * @list_to_deep_free: list to deep free
 
246
 *
 
247
 */
 
248
void
 
249
meta_free_gslist_and_elements (GSList *list_to_deep_free)
 
250
{
 
251
  g_slist_foreach (list_to_deep_free,
 
252
                   (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
 
253
                   NULL);
 
254
  g_slist_free (list_to_deep_free);
 
255
}
 
256
 
 
257
#ifdef WITH_VERBOSE_MODE
 
258
void
 
259
meta_debug_spew_real (const char *format, ...)
 
260
{
 
261
  va_list args;
 
262
  gchar *str;
 
263
  FILE *out;
 
264
 
 
265
  g_return_if_fail (format != NULL);
 
266
 
 
267
  if (!is_debugging)
 
268
    return;
 
269
 
 
270
  va_start (args, format);
 
271
  str = g_strdup_vprintf (format, args);
 
272
  va_end (args);
 
273
 
 
274
  out = logfile ? logfile : stderr;
 
275
 
 
276
  if (no_prefix == 0)
 
277
    utf8_fputs ("Window manager: ", out);
 
278
  utf8_fputs (str, out);
 
279
 
 
280
  fflush (out);
 
281
 
 
282
  g_free (str);
 
283
}
 
284
#endif /* WITH_VERBOSE_MODE */
 
285
 
 
286
#ifdef WITH_VERBOSE_MODE
 
287
void
 
288
meta_verbose_real (const char *format, ...)
 
289
{
 
290
  va_list args;
 
291
 
 
292
  va_start (args, format);
 
293
  meta_topic_real_valist (META_DEBUG_VERBOSE, format, args);
 
294
  va_end (args);
 
295
}
 
296
#endif /* WITH_VERBOSE_MODE */
 
297
 
 
298
#ifdef WITH_VERBOSE_MODE
 
299
static const char*
 
300
topic_name (MetaDebugTopic topic)
 
301
{
 
302
  switch (topic)
 
303
    {
 
304
    case META_DEBUG_FOCUS:
 
305
      return "FOCUS";
 
306
    case META_DEBUG_WORKAREA:
 
307
      return "WORKAREA";
 
308
    case META_DEBUG_STACK:
 
309
      return "STACK";
 
310
    case META_DEBUG_THEMES:
 
311
      return "THEMES";
 
312
    case META_DEBUG_SM:
 
313
      return "SM";
 
314
    case META_DEBUG_EVENTS:
 
315
      return "EVENTS";
 
316
    case META_DEBUG_WINDOW_STATE:
 
317
      return "WINDOW_STATE";
 
318
    case META_DEBUG_WINDOW_OPS:
 
319
      return "WINDOW_OPS";
 
320
    case META_DEBUG_PLACEMENT:
 
321
      return "PLACEMENT";
 
322
    case META_DEBUG_GEOMETRY:
 
323
      return "GEOMETRY";
 
324
    case META_DEBUG_PING:
 
325
      return "PING";
 
326
    case META_DEBUG_XINERAMA:
 
327
      return "XINERAMA";
 
328
    case META_DEBUG_KEYBINDINGS:
 
329
      return "KEYBINDINGS";
 
330
    case META_DEBUG_SYNC:
 
331
      return "SYNC";
 
332
    case META_DEBUG_ERRORS:
 
333
      return "ERRORS";
 
334
    case META_DEBUG_STARTUP:
 
335
      return "STARTUP";
 
336
    case META_DEBUG_PREFS:
 
337
      return "PREFS";
 
338
    case META_DEBUG_GROUPS:
 
339
      return "GROUPS";
 
340
    case META_DEBUG_RESIZING:
 
341
      return "RESIZING";
 
342
    case META_DEBUG_SHAPES:
 
343
      return "SHAPES";
 
344
    case META_DEBUG_COMPOSITOR:
 
345
      return "COMPOSITOR";
 
346
    case META_DEBUG_EDGE_RESISTANCE:
 
347
      return "EDGE_RESISTANCE";
 
348
    case META_DEBUG_DBUS:
 
349
      return "DBUS";
 
350
    case META_DEBUG_VERBOSE:
 
351
      return "VERBOSE";
 
352
    }
 
353
 
 
354
  return "WM";
 
355
}
 
356
 
 
357
static int sync_count = 0;
 
358
 
 
359
static void
 
360
meta_topic_real_valist (MetaDebugTopic topic,
 
361
                        const char    *format,
 
362
                        va_list        args)
 
363
{
 
364
  gchar *str;
 
365
  FILE *out;
 
366
 
 
367
  g_return_if_fail (format != NULL);
 
368
 
 
369
  if (verbose_topics == 0
 
370
      || (topic == META_DEBUG_VERBOSE && verbose_topics != META_DEBUG_VERBOSE)
 
371
      || (!(verbose_topics & topic)))
 
372
    return;
 
373
 
 
374
  str = g_strdup_vprintf (format, args);
 
375
 
 
376
  out = logfile ? logfile : stderr;
 
377
 
 
378
  if (no_prefix == 0)
 
379
    fprintf (out, "%s: ", topic_name (topic));
 
380
 
 
381
  if (topic == META_DEBUG_SYNC)
 
382
    {
 
383
      ++sync_count;
 
384
      fprintf (out, "%d: ", sync_count);
 
385
    }
 
386
 
 
387
  utf8_fputs (str, out);
 
388
 
 
389
  fflush (out);
 
390
 
 
391
  g_free (str);
 
392
}
 
393
 
 
394
void
 
395
meta_topic_real (MetaDebugTopic topic,
 
396
                 const char *format,
 
397
                 ...)
 
398
{
 
399
  va_list args;
 
400
 
 
401
  va_start (args, format);
 
402
  meta_topic_real_valist (topic, format, args);
 
403
  va_end (args);
 
404
}
 
405
#endif /* WITH_VERBOSE_MODE */
 
406
 
 
407
void
 
408
meta_bug (const char *format, ...)
 
409
{
 
410
  va_list args;
 
411
  gchar *str;
 
412
  FILE *out;
 
413
 
 
414
  g_return_if_fail (format != NULL);
 
415
 
 
416
  va_start (args, format);
 
417
  str = g_strdup_vprintf (format, args);
 
418
  va_end (args);
 
419
 
 
420
#ifdef WITH_VERBOSE_MODE
 
421
  out = logfile ? logfile : stderr;
 
422
#else
 
423
  out = stderr;
 
424
#endif
 
425
 
 
426
  if (no_prefix == 0)
 
427
    utf8_fputs ("Bug in window manager: ", out);
 
428
  utf8_fputs (str, out);
 
429
 
 
430
  fflush (out);
 
431
 
 
432
  g_free (str);
 
433
 
 
434
  /* stop us in a debugger */
 
435
  abort ();
 
436
}
 
437
 
 
438
void
 
439
meta_warning (const char *format, ...)
 
440
{
 
441
  va_list args;
 
442
  gchar *str;
 
443
  FILE *out;
 
444
 
 
445
  g_return_if_fail (format != NULL);
 
446
 
 
447
  va_start (args, format);
 
448
  str = g_strdup_vprintf (format, args);
 
449
  va_end (args);
 
450
 
 
451
#ifdef WITH_VERBOSE_MODE
 
452
  out = logfile ? logfile : stderr;
 
453
#else
 
454
  out = stderr;
 
455
#endif
 
456
 
 
457
  if (no_prefix == 0)
 
458
    utf8_fputs ("Window manager warning: ", out);
 
459
  utf8_fputs (str, out);
 
460
 
 
461
  fflush (out);
 
462
 
 
463
  g_free (str);
 
464
}
 
465
 
 
466
void
 
467
meta_fatal (const char *format, ...)
 
468
{
 
469
  va_list args;
 
470
  gchar *str;
 
471
  FILE *out;
 
472
 
 
473
  g_warn_if_fail (format);
 
474
  if (!format)
 
475
    meta_exit (META_EXIT_ERROR);
 
476
 
 
477
  va_start (args, format);
 
478
  str = g_strdup_vprintf (format, args);
 
479
  va_end (args);
 
480
 
 
481
#ifdef WITH_VERBOSE_MODE
 
482
  out = logfile ? logfile : stderr;
 
483
#else
 
484
  out = stderr;
 
485
#endif
 
486
 
 
487
  if (no_prefix == 0)
 
488
    utf8_fputs ("Window manager error: ", out);
 
489
  utf8_fputs (str, out);
 
490
 
 
491
  fflush (out);
 
492
 
 
493
  g_free (str);
 
494
 
 
495
  meta_exit (META_EXIT_ERROR);
 
496
}
 
497
 
 
498
void
 
499
meta_push_no_msg_prefix (void)
 
500
{
 
501
  ++no_prefix;
 
502
}
 
503
 
 
504
void
 
505
meta_pop_no_msg_prefix (void)
 
506
{
 
507
  g_return_if_fail (no_prefix > 0);
 
508
 
 
509
  --no_prefix;
 
510
}
 
511
 
 
512
void
 
513
meta_exit (MetaExitCode code)
 
514
{
 
515
 
 
516
  exit (code);
 
517
}
 
518
 
 
519
gint
 
520
meta_unsigned_long_equal (gconstpointer v1,
 
521
                          gconstpointer v2)
 
522
{
 
523
  return *((const gulong*) v1) == *((const gulong*) v2);
 
524
}
 
525
 
 
526
guint
 
527
meta_unsigned_long_hash  (gconstpointer v)
 
528
{
 
529
  gulong val = * (const gulong *) v;
 
530
 
 
531
  /* I'm not sure this works so well. */
 
532
#if GLIB_SIZEOF_LONG > 4
 
533
  return (guint) (val ^ (val >> 32));
 
534
#else
 
535
  return val;
 
536
#endif
 
537
}
 
538
 
 
539
const char*
 
540
meta_gravity_to_string (int gravity)
 
541
{
 
542
  switch (gravity)
 
543
    {
 
544
    case NorthWestGravity:
 
545
      return "NorthWestGravity";
 
546
      break;
 
547
    case NorthGravity:
 
548
      return "NorthGravity";
 
549
      break;
 
550
    case NorthEastGravity:
 
551
      return "NorthEastGravity";
 
552
      break;
 
553
    case WestGravity:
 
554
      return "WestGravity";
 
555
      break;
 
556
    case CenterGravity:
 
557
      return "CenterGravity";
 
558
      break;
 
559
    case EastGravity:
 
560
      return "EastGravity";
 
561
      break;
 
562
    case SouthWestGravity:
 
563
      return "SouthWestGravity";
 
564
      break;
 
565
    case SouthGravity:
 
566
      return "SouthGravity";
 
567
      break;
 
568
    case SouthEastGravity:
 
569
      return "SouthEastGravity";
 
570
      break;
 
571
    case StaticGravity:
 
572
      return "StaticGravity";
 
573
      break;
 
574
    default:
 
575
      return "NorthWestGravity";
 
576
      break;
 
577
    }
 
578
}
 
579
 
 
580
char*
 
581
meta_external_binding_name_for_action (guint keybinding_action)
 
582
{
 
583
  return g_strdup_printf ("external-grab-%u", keybinding_action);
 
584
}
 
585
 
 
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.
 
590
 */
 
591
static void
 
592
append_argument (GPtrArray  *args,
 
593
                 const char *arg)
 
594
{
 
595
  char *locale_arg = g_locale_from_utf8 (arg, -1, NULL, NULL, NULL);
 
596
 
 
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 */
 
599
  if (!locale_arg)
 
600
    locale_arg = g_strdup ("???");
 
601
 
 
602
  g_ptr_array_add (args, locale_arg);
 
603
}
 
604
 
 
605
/**
 
606
 * meta_show_dialog: (skip)
 
607
 * @type: type of dialog
 
608
 * @message: message
 
609
 * @timeout: timeout
 
610
 * @display: display
 
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
 
615
 * @columns: columns
 
616
 * @entries: entries
 
617
 *
 
618
 */
 
619
GPid
 
620
meta_show_dialog (const char *type,
 
621
                  const char *message,
 
622
                  const char *timeout,
 
623
                  const char *display,
 
624
                  const char *ok_text,
 
625
                  const char *cancel_text,
 
626
                  const char *icon_name,
 
627
                  const int transient_for,
 
628
                  GSList *columns,
 
629
                  GSList *entries)
 
630
{
 
631
  GError *error = NULL;
 
632
  GSList *tmp;
 
633
  GPid child_pid;
 
634
  GPtrArray *args;
 
635
 
 
636
  args = g_ptr_array_new ();
 
637
 
 
638
  append_argument (args, "zenity");
 
639
  append_argument (args, type);
 
640
 
 
641
  if (display)
 
642
    {
 
643
      append_argument (args, "--display");
 
644
      append_argument (args, display);
 
645
    }
 
646
 
 
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);
 
653
 
 
654
  if (timeout)
 
655
    {
 
656
      append_argument (args, "--timeout");
 
657
      append_argument (args, timeout);
 
658
    }
 
659
 
 
660
  if (ok_text)
 
661
    {
 
662
      append_argument (args, "--ok-label");
 
663
      append_argument (args, ok_text);
 
664
     }
 
665
 
 
666
  if (cancel_text)
 
667
    {
 
668
      append_argument (args, "--cancel-label");
 
669
      append_argument (args, cancel_text);
 
670
    }
 
671
 
 
672
  if (icon_name)
 
673
    {
 
674
      append_argument (args, "--icon-name");
 
675
      append_argument (args, icon_name);
 
676
    }
 
677
 
 
678
  tmp = columns;
 
679
  while (tmp)
 
680
    {
 
681
      append_argument (args, "--column");
 
682
      append_argument (args, tmp->data);
 
683
      tmp = tmp->next;
 
684
    }
 
685
 
 
686
  tmp = entries;
 
687
  while (tmp)
 
688
    {
 
689
      append_argument (args, tmp->data);
 
690
      tmp = tmp->next;
 
691
    }
 
692
 
 
693
  if (transient_for)
 
694
    {
 
695
      gchar *env = g_strdup_printf("%d", transient_for);
 
696
      setenv ("WINDOWID", env, 1);
 
697
      g_free (env);
 
698
 
 
699
      append_argument (args, "--modal");
 
700
    }
 
701
 
 
702
  g_ptr_array_add (args, NULL); /* NULL-terminate */
 
703
 
 
704
  g_spawn_async (
 
705
                 "/",
 
706
                 (gchar**) args->pdata,
 
707
                 NULL,
 
708
                 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
 
709
                 NULL, NULL,
 
710
                 &child_pid,
 
711
                 &error
 
712
                 );
 
713
 
 
714
  if (transient_for)
 
715
    unsetenv ("WINDOWID");
 
716
 
 
717
  g_ptr_array_free (args, TRUE);
 
718
 
 
719
  if (error)
 
720
    {
 
721
      meta_warning ("%s\n", error->message);
 
722
      g_error_free (error);
 
723
    }
 
724
 
 
725
  return child_pid;
 
726
}
 
727
 
 
728
/***************************************************************************
 
729
 * Later functions: like idles but integrated with the Clutter repaint loop
 
730
 ***************************************************************************/
 
731
 
 
732
static guint last_later_id = 0;
 
733
 
 
734
typedef struct
 
735
{
 
736
  guint id;
 
737
  guint ref_count;
 
738
  MetaLaterType when;
 
739
  GSourceFunc func;
 
740
  gpointer data;
 
741
  GDestroyNotify notify;
 
742
  int source;
 
743
  gboolean run_once;
 
744
} MetaLater;
 
745
 
 
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 */
 
753
};
 
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;
 
757
 
 
758
static void ensure_later_repaint_func (void);
 
759
 
 
760
static void
 
761
unref_later (MetaLater *later)
 
762
{
 
763
  if (--later->ref_count == 0)
 
764
    {
 
765
      if (later->notify)
 
766
        {
 
767
          later->notify (later->data);
 
768
          later->notify = NULL;
 
769
        }
 
770
      g_slice_free (MetaLater, later);
 
771
    }
 
772
}
 
773
 
 
774
static void
 
775
destroy_later (MetaLater *later)
 
776
{
 
777
  if (later->source)
 
778
    {
 
779
      g_source_remove (later->source);
 
780
      later->source = 0;
 
781
    }
 
782
  later->func = NULL;
 
783
  unref_later (later);
 
784
}
 
785
 
 
786
static void
 
787
run_repaint_laters (GSList **laters_list)
 
788
{
 
789
  GSList *laters_copy;
 
790
  GSList *l;
 
791
 
 
792
  laters_copy = NULL;
 
793
  for (l = *laters_list; l; l = l->next)
 
794
    {
 
795
      MetaLater *later = l->data;
 
796
      if (later->source == 0 ||
 
797
          (later->when <= META_LATER_BEFORE_REDRAW && !later->run_once))
 
798
        {
 
799
          later->ref_count++;
 
800
          laters_copy = g_slist_prepend (laters_copy, later);
 
801
        }
 
802
    }
 
803
  laters_copy = g_slist_reverse (laters_copy);
 
804
 
 
805
  for (l = laters_copy; l; l = l->next)
 
806
    {
 
807
      MetaLater *later = l->data;
 
808
 
 
809
      if (!later->func || !later->func (later->data))
 
810
        meta_later_remove_from_list (later->id, laters_list);
 
811
      unref_later (later);
 
812
    }
 
813
 
 
814
  g_slist_free (laters_copy);
 
815
}
 
816
 
 
817
static gboolean
 
818
run_all_repaint_laters (gpointer data)
 
819
{
 
820
  guint i;
 
821
  GSList *l;
 
822
  gboolean keep_timeline_running = FALSE;
 
823
 
 
824
  for (i = 0; i < G_N_ELEMENTS (laters); i++)
 
825
    {
 
826
      run_repaint_laters (&laters[i]);
 
827
    }
 
828
 
 
829
  for (i = 0; i < G_N_ELEMENTS (laters); i++)
 
830
    {
 
831
      for (l = laters[i]; l; l = l->next)
 
832
        {
 
833
          MetaLater *later = l->data;
 
834
 
 
835
          if (later->source == 0)
 
836
            keep_timeline_running = TRUE;
 
837
        }
 
838
    }
 
839
 
 
840
  if (!keep_timeline_running)
 
841
    clutter_timeline_stop (later_timeline);
 
842
 
 
843
  /* Just keep the repaint func around - it's cheap if the lists are empty */
 
844
  return TRUE;
 
845
}
 
846
 
 
847
static void
 
848
ensure_later_repaint_func (void)
 
849
{
 
850
  if (!later_timeline)
 
851
    later_timeline = clutter_timeline_new (G_MAXUINT);
 
852
 
 
853
  if (later_repaint_func == 0)
 
854
    later_repaint_func = clutter_threads_add_repaint_func (run_all_repaint_laters,
 
855
                                                           NULL, NULL);
 
856
 
 
857
  /* Make sure the repaint function gets run */
 
858
  clutter_timeline_start (later_timeline);
 
859
}
 
860
 
 
861
static gboolean
 
862
call_idle_later (gpointer data)
 
863
{
 
864
  MetaLater *later = data;
 
865
 
 
866
  if (!later->func (later->data))
 
867
    {
 
868
      meta_later_remove (later->id);
 
869
      return FALSE;
 
870
    }
 
871
  else
 
872
    {
 
873
      later->run_once = TRUE;
 
874
      return TRUE;
 
875
    }
 
876
}
 
877
 
 
878
/**
 
879
 * meta_later_add:
 
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
 
884
 *
 
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.
 
891
 *
 
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.
 
894
 */
 
895
guint
 
896
meta_later_add (MetaLaterType  when,
 
897
                GSourceFunc    func,
 
898
                gpointer       data,
 
899
                GDestroyNotify notify)
 
900
{
 
901
  MetaLater *later = g_slice_new0 (MetaLater);
 
902
 
 
903
  later->id = ++last_later_id;
 
904
  later->ref_count = 1;
 
905
  later->when = when;
 
906
  later->func = func;
 
907
  later->data = data;
 
908
  later->notify = notify;
 
909
 
 
910
  laters[when] = g_slist_prepend (laters[when], later);
 
911
 
 
912
  switch (when)
 
913
    {
 
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.
 
922
       */
 
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 ();
 
926
      break;
 
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 ();
 
932
      break;
 
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");
 
936
      break;
 
937
    }
 
938
 
 
939
  return later->id;
 
940
}
 
941
 
 
942
static gboolean
 
943
meta_later_remove_from_list (guint later_id, GSList **laters_list)
 
944
{
 
945
  GSList *l;
 
946
 
 
947
  for (l = *laters_list; l; l = l->next)
 
948
    {
 
949
      MetaLater *later = l->data;
 
950
 
 
951
      if (later->id == later_id)
 
952
        {
 
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
 
956
           */
 
957
          destroy_later (later);
 
958
          return TRUE;
 
959
        }
 
960
    }
 
961
 
 
962
  return FALSE;
 
963
}
 
964
 
 
965
/**
 
966
 * meta_later_remove:
 
967
 * @later_id: the integer ID returned from meta_later_add()
 
968
 *
 
969
 * Removes a callback added with meta_later_add()
 
970
 */
 
971
void
 
972
meta_later_remove (guint later_id)
 
973
{
 
974
  guint i;
 
975
 
 
976
  for (i = 0; i < G_N_ELEMENTS (laters); i++)
 
977
    {
 
978
      if (meta_later_remove_from_list (later_id, &laters[i]))
 
979
        return;
 
980
    }
 
981
}
 
982
 
 
983
MetaLocaleDirection
 
984
meta_get_locale_direction (void)
 
985
{
 
986
  switch (gtk_get_locale_direction ())
 
987
    {
 
988
    case GTK_TEXT_DIR_LTR:
 
989
      return META_LOCALE_DIRECTION_LTR;
 
990
    case GTK_TEXT_DIR_RTL:
 
991
      return META_LOCALE_DIRECTION_RTL;
 
992
    default:
 
993
      g_assert_not_reached ();
 
994
    }
 
995
}
 
996
 
 
997
char *
 
998
meta_generate_random_id (GRand *rand,
 
999
                         int    length)
 
1000
{
 
1001
  char *id;
 
1002
  int i;
 
1003
 
 
1004
  /* Generate a random string of printable ASCII characters. */
 
1005
 
 
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);
 
1009
 
 
1010
  return id;
 
1011
}
 
1012
 
 
1013
/* eof util.c */
 
1014