1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Metacity interface for talking to GTK+ UI module */
4 
5 /*
6  * Copyright (C) 2002 Havoc Pennington
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include "prefs.h"
25 #include "ui.h"
26 #include "ui-private.h"
27 #include "frames.h"
28 #include "util.h"
29 #include "menu.h"
30 #include "core.h"
31 
32 #include <string.h>
33 #include <stdlib.h>
34 #include <cairo-xlib.h>
35 #include <libmetacity/meta-theme.h>
36 
37 #define META_DEFAULT_ICON_NAME "window"
38 
39 struct _MetaUI
40 {
41   Display *xdisplay;
42 
43   gboolean composited;
44   gint scale;
45   gdouble dpi;
46 
47   MetaTheme *theme;
48   MetaFrames *frames;
49 
50   /* For double-click tracking */
51   guint button_click_number;
52   Window button_click_window;
53   int button_click_x;
54   int button_click_y;
55   guint32 button_click_time;
56 };
57 
58 static gboolean
get_int_setting(const gchar * name,gint * value)59 get_int_setting (const gchar *name,
60                  gint        *value)
61 {
62   GValue gvalue = G_VALUE_INIT;
63 
64   g_value_init (&gvalue, G_TYPE_INT);
65 
66   if (gdk_screen_get_setting (gdk_screen_get_default (), name, &gvalue))
67     {
68       *value = g_value_get_int (&gvalue);
69       return TRUE;
70     }
71 
72   return FALSE;
73 }
74 
75 static gint
get_window_scaling_factor(MetaUI * ui)76 get_window_scaling_factor (MetaUI *ui)
77 {
78   gint scale;
79 
80   if (get_int_setting ("gdk-window-scaling-factor", &scale))
81     return scale;
82 
83   return 1;
84 }
85 
86 static gdouble
get_xft_dpi(MetaUI * ui)87 get_xft_dpi (MetaUI *ui)
88 {
89   const gchar *dpi_scale_env;
90   gint xft_dpi;
91   gdouble dpi;
92 
93   dpi = 96.0;
94   if (get_int_setting ("gtk-xft-dpi", &xft_dpi) && xft_dpi > 0)
95     dpi = xft_dpi / 1024.0 / ui->scale;
96 
97   dpi_scale_env = g_getenv ("GDK_DPI_SCALE");
98   if (dpi_scale_env != NULL)
99     {
100       gdouble dpi_scale;
101 
102       dpi_scale = g_ascii_strtod (dpi_scale_env, NULL);
103       if (dpi_scale != 0)
104         dpi *= dpi_scale;
105     }
106 
107   return dpi;
108 }
109 
110 static void
notify_gtk_xft_dpi_cb(GtkSettings * settings,GParamSpec * pspec,MetaUI * ui)111 notify_gtk_xft_dpi_cb (GtkSettings *settings,
112                        GParamSpec  *pspec,
113                        MetaUI      *ui)
114 {
115   ui->scale = get_window_scaling_factor (ui);
116   ui->dpi = get_xft_dpi (ui);
117 
118   meta_theme_set_scale (ui->theme, ui->scale);
119   meta_theme_set_dpi (ui->theme, ui->dpi);
120 }
121 
122 void
meta_ui_init(int * argc,char *** argv)123 meta_ui_init (int *argc, char ***argv)
124 {
125   /* As of 2.91.7, Gdk uses XI2 by default, which conflicts with the
126    * direct X calls we use - in particular, events caused by calls to
127    * XGrabPointer/XGrabKeyboard are no longer understood by GDK, while
128    * GDK will no longer generate the core XEvents we process.
129    * So at least for now, enforce the previous behavior.
130    */
131 #if GTK_CHECK_VERSION(2, 91, 7)
132   gdk_disable_multidevice ();
133 #endif
134 
135   gdk_set_allowed_backends ("x11");
136 
137   if (!gtk_init_check (argc, argv))
138     {
139       g_critical ("Unable to open X display %s", XDisplayName (NULL));
140       exit (EXIT_FAILURE);
141     }
142 
143   /* We need to be able to fully trust that the window and monitor sizes
144    * that GDK reports corresponds to the X ones, so we disable the automatic
145    * scale handling */
146   gdk_x11_display_set_window_scale (gdk_display_get_default (), 1);
147 }
148 
149 Display*
meta_ui_get_display(void)150 meta_ui_get_display (void)
151 {
152   return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
153 }
154 
155 /* We do some of our event handling in frames.c, which expects
156  * GDK events delivered by GTK+.  However, since the transition to
157  * client side windows, we can't let GDK see button events, since the
158  * client-side tracking of implicit and explicit grabs it does will
159  * get confused by our direct use of X grabs in the core code.
160  *
161  * So we do a very minimal GDK => GTK event conversion here and send on the
162  * events we care about, and then filter them out so they don't go
163  * through the normal GDK event handling.
164  *
165  * To reduce the amount of code, the only events fields filled out
166  * below are the ones that frames.c uses. If frames.c is modified to
167  * use more fields, more fields need to be filled out below.
168  */
169 
170 static gboolean
maybe_redirect_mouse_event(XEvent * xevent)171 maybe_redirect_mouse_event (XEvent *xevent)
172 {
173   GdkDisplay *gdisplay;
174   GdkSeat *seat;
175   GdkDevice *gdevice;
176   MetaUI *ui;
177   GdkEvent *gevent;
178   GdkWindow *gdk_window;
179   Window window;
180 
181   switch (xevent->type)
182     {
183     case ButtonPress:
184     case ButtonRelease:
185       window = xevent->xbutton.window;
186       break;
187     case MotionNotify:
188       window = xevent->xmotion.window;
189       break;
190     case EnterNotify:
191     case LeaveNotify:
192       window = xevent->xcrossing.window;
193       break;
194     default:
195       return FALSE;
196     }
197 
198   gdisplay = gdk_x11_lookup_xdisplay (xevent->xany.display);
199   ui = g_object_get_data (G_OBJECT (gdisplay), "meta-ui");
200   if (!ui)
201     return FALSE;
202 
203   gdk_window = gdk_x11_window_lookup_for_display (gdisplay, window);
204   if (gdk_window == NULL)
205     return FALSE;
206 
207   seat = gdk_display_get_default_seat (gdisplay);
208   gdevice = gdk_seat_get_pointer (seat);
209 
210   /* If GDK already thinks it has a grab, we better let it see events; this
211    * is the menu-navigation case and events need to get sent to the appropriate
212    * (client-side) subwindow for individual menu items.
213    */
214   if (gdk_display_device_is_grabbed (gdisplay, gdevice))
215     return FALSE;
216 
217   switch (xevent->type)
218     {
219     case ButtonPress:
220     case ButtonRelease:
221       if (xevent->type == ButtonPress)
222         {
223           GtkSettings *settings = gtk_settings_get_default ();
224           int double_click_time;
225           int double_click_distance;
226 
227           g_object_get (settings,
228                         "gtk-double-click-time", &double_click_time,
229                         "gtk-double-click-distance", &double_click_distance,
230                         NULL);
231 
232           if (xevent->xbutton.button == ui->button_click_number &&
233               xevent->xbutton.window == ui->button_click_window &&
234               xevent->xbutton.time < ui->button_click_time + double_click_time &&
235               ABS (xevent->xbutton.x - ui->button_click_x) <= double_click_distance &&
236               ABS (xevent->xbutton.y - ui->button_click_y) <= double_click_distance)
237             {
238               gevent = gdk_event_new (GDK_2BUTTON_PRESS);
239 
240               ui->button_click_number = 0;
241             }
242           else
243             {
244               gevent = gdk_event_new (GDK_BUTTON_PRESS);
245               ui->button_click_number = xevent->xbutton.button;
246               ui->button_click_window = xevent->xbutton.window;
247               ui->button_click_time = xevent->xbutton.time;
248               ui->button_click_x = xevent->xbutton.x;
249               ui->button_click_y = xevent->xbutton.y;
250             }
251         }
252       else
253         {
254           gevent = gdk_event_new (GDK_BUTTON_RELEASE);
255         }
256 
257       gevent->button.window = g_object_ref (gdk_window);
258       gevent->button.button = xevent->xbutton.button;
259       gevent->button.time = xevent->xbutton.time;
260       gevent->button.x = xevent->xbutton.x;
261       gevent->button.y = xevent->xbutton.y;
262       gevent->button.x_root = xevent->xbutton.x_root;
263       gevent->button.y_root = xevent->xbutton.y_root;
264 
265       break;
266     case MotionNotify:
267       gevent = gdk_event_new (GDK_MOTION_NOTIFY);
268       gevent->motion.type = GDK_MOTION_NOTIFY;
269       gevent->motion.window = g_object_ref (gdk_window);
270       break;
271     case EnterNotify:
272     case LeaveNotify:
273       gevent = gdk_event_new (xevent->type == EnterNotify ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY);
274       gevent->crossing.window = g_object_ref (gdk_window);
275       gevent->crossing.x = xevent->xcrossing.x;
276       gevent->crossing.y = xevent->xcrossing.y;
277       break;
278     default:
279       g_assert_not_reached ();
280       break;
281     }
282 
283   /* If we've gotten here, we've filled in the gdk_event and should send it on */
284   gdk_event_set_device (gevent, gdevice);
285   gtk_main_do_event (gevent);
286   gdk_event_free (gevent);
287 
288   return TRUE;
289 }
290 
291 typedef struct _EventFunc EventFunc;
292 
293 struct _EventFunc
294 {
295   MetaEventFunc func;
296   gpointer data;
297 };
298 
299 static EventFunc *ef = NULL;
300 
301 static GdkFilterReturn
filter_func(GdkXEvent * xevent,GdkEvent * event,gpointer data)302 filter_func (GdkXEvent *xevent,
303              GdkEvent *event,
304              gpointer data)
305 {
306   g_return_val_if_fail (ef != NULL, GDK_FILTER_CONTINUE);
307 
308   if ((* ef->func) (xevent, ef->data) ||
309       maybe_redirect_mouse_event (xevent))
310     return GDK_FILTER_REMOVE;
311   else
312     return GDK_FILTER_CONTINUE;
313 }
314 
315 void
meta_ui_add_event_func(Display * xdisplay,MetaEventFunc func,gpointer data)316 meta_ui_add_event_func (Display       *xdisplay,
317                         MetaEventFunc  func,
318                         gpointer       data)
319 {
320   g_return_if_fail (ef == NULL);
321 
322   ef = g_new (EventFunc, 1);
323   ef->func = func;
324   ef->data = data;
325 
326   gdk_window_add_filter (NULL, filter_func, ef);
327 }
328 
329 /* removal is by data due to proxy function */
330 void
meta_ui_remove_event_func(Display * xdisplay,MetaEventFunc func,gpointer data)331 meta_ui_remove_event_func (Display       *xdisplay,
332                            MetaEventFunc  func,
333                            gpointer       data)
334 {
335   g_return_if_fail (ef != NULL);
336 
337   gdk_window_remove_filter (NULL, filter_func, ef);
338 
339   g_free (ef);
340   ef = NULL;
341 }
342 
343 MetaUI*
meta_ui_new(Display * xdisplay,gboolean composited)344 meta_ui_new (Display  *xdisplay,
345              gboolean  composited)
346 {
347   GdkDisplay *gdisplay;
348   MetaUI *ui;
349 
350   ui = g_new0 (MetaUI, 1);
351   ui->xdisplay = xdisplay;
352 
353   ui->composited = composited;
354   ui->scale = get_window_scaling_factor (ui);
355   ui->dpi = get_xft_dpi (ui);
356 
357   g_signal_connect (gtk_settings_get_default (), "notify::gtk-xft-dpi",
358                     G_CALLBACK (notify_gtk_xft_dpi_cb), ui);
359 
360   gdisplay = gdk_x11_lookup_xdisplay (xdisplay);
361   g_assert (gdisplay == gdk_display_get_default ());
362   g_assert (xdisplay == GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
363 
364   meta_ui_reload_theme (ui);
365 
366   ui->frames = meta_frames_new (ui);
367 
368   /* GTK+ needs the frame-sync protocol to work in order to properly
369    * handle style changes. This means that the dummy widget we create
370    * to get the style for title bars actually needs to be mapped
371    * and fully tracked as a MetaWindow. Horrible, but mostly harmless -
372    * the window is a 1x1 overide redirect window positioned offscreen.
373    */
374   gtk_widget_show (GTK_WIDGET (ui->frames));
375 
376   g_object_set_data (G_OBJECT (gdisplay), "meta-ui", ui);
377 
378   return ui;
379 }
380 
381 void
meta_ui_free(MetaUI * ui)382 meta_ui_free (MetaUI *ui)
383 {
384   GdkDisplay *gdisplay;
385 
386   gtk_widget_destroy (GTK_WIDGET (ui->frames));
387 
388   gdisplay = gdk_x11_lookup_xdisplay (ui->xdisplay);
389   g_object_set_data (G_OBJECT (gdisplay), "meta-ui", NULL);
390 
391   g_free (ui);
392 }
393 
394 void
meta_ui_set_composited(MetaUI * ui,gboolean composited)395 meta_ui_set_composited (MetaUI   *ui,
396                         gboolean  composited)
397 {
398   if (ui->composited == composited)
399     return;
400 
401   ui->composited = composited;
402 
403   meta_theme_set_composited (ui->theme, composited);
404   meta_frames_composited_changed (ui->frames);
405 }
406 
407 gint
meta_ui_get_scale(MetaUI * ui)408 meta_ui_get_scale (MetaUI *ui)
409 {
410   return ui->scale;
411 }
412 
413 void
meta_ui_get_frame_borders(MetaUI * ui,Window frame_xwindow,MetaFrameBorders * borders)414 meta_ui_get_frame_borders (MetaUI *ui,
415                            Window frame_xwindow,
416                            MetaFrameBorders *borders)
417 {
418   meta_frames_get_borders (ui->frames, frame_xwindow, borders);
419 }
420 
421 static void
set_background_none(Display * xdisplay,Window xwindow)422 set_background_none (Display *xdisplay,
423                      Window   xwindow)
424 {
425   XSetWindowAttributes attrs;
426 
427   attrs.background_pixmap = None;
428   XChangeWindowAttributes (xdisplay, xwindow,
429                            CWBackPixmap, &attrs);
430 }
431 
432 Window
meta_ui_create_frame_window(MetaUI * ui,Display * xdisplay,Visual * xvisual,gint x,gint y,gint width,gint height,gulong * create_serial)433 meta_ui_create_frame_window (MetaUI  *ui,
434                              Display *xdisplay,
435                              Visual  *xvisual,
436                              gint     x,
437                              gint     y,
438                              gint     width,
439                              gint     height,
440                              gulong  *create_serial)
441 {
442   GdkScreen *screen = gdk_screen_get_default ();
443   GdkWindowAttr attrs;
444   gint attributes_mask;
445   GdkWindow *window;
446   GdkVisual *visual;
447 
448   /* Default depth/visual handles clients with weird visuals; they can
449    * always be children of the root depth/visual obviously, but
450    * e.g. DRI games can't be children of a parent that has the same
451    * visual as the client.
452    */
453   if (!xvisual)
454     visual = gdk_screen_get_system_visual (screen);
455   else
456     {
457       visual = gdk_x11_screen_lookup_visual (screen,
458                                              XVisualIDFromVisual (xvisual));
459     }
460 
461   attrs.title = NULL;
462 
463   /* frame.c is going to replace the event mask immediately, but
464    * we still have to set it here to let GDK know what it is.
465    */
466   attrs.event_mask =
467     GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
468     GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
469     GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK;
470   attrs.x = x;
471   attrs.y = y;
472   attrs.wclass = GDK_INPUT_OUTPUT;
473   attrs.visual = visual;
474   attrs.window_type = GDK_WINDOW_CHILD;
475   attrs.cursor = NULL;
476   attrs.wmclass_name = NULL;
477   attrs.wmclass_class = NULL;
478   attrs.override_redirect = FALSE;
479 
480   attrs.width  = width;
481   attrs.height = height;
482 
483   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
484 
485   /* We make an assumption that gdk_window_new() is going to call
486    * XCreateWindow as it's first operation; this seems to be true currently
487    * as long as you pass in a colormap.
488    */
489   if (create_serial)
490     *create_serial = XNextRequest (xdisplay);
491   window =
492     gdk_window_new (gdk_screen_get_root_window(screen),
493 		    &attrs, attributes_mask);
494 
495   gdk_window_resize (window, width, height);
496   set_background_none (xdisplay, GDK_WINDOW_XID (window));
497 
498   meta_frames_manage_window (ui->frames, GDK_WINDOW_XID (window), window);
499 
500   return GDK_WINDOW_XID (window);
501 }
502 
503 void
meta_ui_destroy_frame_window(MetaUI * ui,Window xwindow)504 meta_ui_destroy_frame_window (MetaUI *ui,
505 			      Window  xwindow)
506 {
507   meta_frames_unmanage_window (ui->frames, xwindow);
508 }
509 
510 void
meta_ui_move_resize_frame(MetaUI * ui,Window frame,int x,int y,int width,int height)511 meta_ui_move_resize_frame (MetaUI *ui,
512 			   Window frame,
513 			   int x,
514 			   int y,
515 			   int width,
516 			   int height)
517 {
518   meta_frames_move_resize_frame (ui->frames, frame, x, y, width, height);
519 }
520 
521 void
meta_ui_map_frame(MetaUI * ui,Window xwindow)522 meta_ui_map_frame   (MetaUI *ui,
523                      Window  xwindow)
524 {
525   GdkWindow *window;
526   GdkDisplay *display;
527 
528   display = gdk_x11_lookup_xdisplay (ui->xdisplay);
529   window = gdk_x11_window_lookup_for_display (display, xwindow);
530 
531   if (window)
532     gdk_window_show_unraised (window);
533 }
534 
535 void
meta_ui_unmap_frame(MetaUI * ui,Window xwindow)536 meta_ui_unmap_frame (MetaUI *ui,
537                      Window  xwindow)
538 {
539   GdkWindow *window;
540   GdkDisplay *display;
541 
542   display = gdk_x11_lookup_xdisplay (ui->xdisplay);
543   window = gdk_x11_window_lookup_for_display (display, xwindow);
544 
545   if (window)
546     gdk_window_hide (window);
547 }
548 
549 void
meta_ui_update_frame_style(MetaUI * ui,Window xwindow)550 meta_ui_update_frame_style (MetaUI *ui,
551                             Window  xwindow)
552 {
553   meta_frames_update_frame_style (ui->frames, xwindow);
554 }
555 
556 void
meta_ui_repaint_frame(MetaUI * ui,Window xwindow)557 meta_ui_repaint_frame (MetaUI *ui,
558                        Window xwindow)
559 {
560   meta_frames_repaint_frame (ui->frames, xwindow);
561 }
562 
563 void
meta_ui_apply_frame_shape(MetaUI * ui,Window xwindow,int new_window_width,int new_window_height,gboolean window_has_shape)564 meta_ui_apply_frame_shape  (MetaUI  *ui,
565                             Window   xwindow,
566                             int      new_window_width,
567                             int      new_window_height,
568                             gboolean window_has_shape)
569 {
570   meta_frames_apply_shapes (ui->frames, xwindow,
571                             new_window_width, new_window_height,
572                             window_has_shape);
573 }
574 
575 cairo_region_t *
meta_ui_get_frame_bounds(MetaUI * ui,Window xwindow,int window_width,int window_height)576 meta_ui_get_frame_bounds (MetaUI *ui,
577                           Window  xwindow,
578                           int     window_width,
579                           int     window_height)
580 {
581   return meta_frames_get_frame_bounds (ui->frames,
582                                        xwindow,
583                                        window_width,
584                                        window_height);
585 }
586 
587 void
meta_ui_queue_frame_draw(MetaUI * ui,Window xwindow)588 meta_ui_queue_frame_draw (MetaUI *ui,
589                           Window xwindow)
590 {
591   meta_frames_queue_draw (ui->frames, xwindow);
592 }
593 
594 
595 void
meta_ui_set_frame_title(MetaUI * ui,Window xwindow,const char * title)596 meta_ui_set_frame_title (MetaUI     *ui,
597                          Window      xwindow,
598                          const char *title)
599 {
600   meta_frames_set_title (ui->frames, xwindow, title);
601 }
602 
603 MetaWindowMenu*
meta_ui_window_menu_new(MetaUI * ui,Window client_xwindow,MetaMenuOp ops,MetaMenuOp insensitive,unsigned long active_workspace,int n_workspaces,MetaWindowMenuFunc func,gpointer data)604 meta_ui_window_menu_new  (MetaUI             *ui,
605                           Window              client_xwindow,
606                           MetaMenuOp          ops,
607                           MetaMenuOp          insensitive,
608                           unsigned long       active_workspace,
609                           int                 n_workspaces,
610                           MetaWindowMenuFunc  func,
611                           gpointer            data)
612 {
613   return meta_window_menu_new (ui->frames,
614                                ops, insensitive,
615                                client_xwindow,
616                                active_workspace,
617                                n_workspaces,
618                                func, data);
619 }
620 
621 void
meta_ui_window_menu_popup(MetaWindowMenu * menu,const GdkRectangle * rect,guint32 timestamp)622 meta_ui_window_menu_popup (MetaWindowMenu     *menu,
623                            const GdkRectangle *rect,
624                            guint32             timestamp)
625 {
626   meta_window_menu_popup (menu, rect, timestamp);
627 }
628 
629 void
meta_ui_window_menu_free(MetaWindowMenu * menu)630 meta_ui_window_menu_free (MetaWindowMenu *menu)
631 {
632   meta_window_menu_free (menu);
633 }
634 
635 GdkPixbuf*
meta_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap,int src_x,int src_y,int width,int height)636 meta_gdk_pixbuf_get_from_pixmap (Pixmap       xpixmap,
637                                  int          src_x,
638                                  int          src_y,
639                                  int          width,
640                                  int          height)
641 {
642   cairo_surface_t *surface;
643   Display *display;
644   Window root_return;
645   int x_ret, y_ret;
646   unsigned int w_ret, h_ret, bw_ret, depth_ret;
647   XWindowAttributes attrs;
648   GdkPixbuf *retval;
649 
650   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
651 
652   if (!XGetGeometry (display, xpixmap, &root_return,
653                      &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret))
654     return NULL;
655 
656   if (depth_ret == 1)
657     {
658       surface = cairo_xlib_surface_create_for_bitmap (display,
659                                                       xpixmap,
660                                                       GDK_SCREEN_XSCREEN (gdk_screen_get_default ()),
661                                                       w_ret,
662                                                       h_ret);
663     }
664   else
665     {
666       if (!XGetWindowAttributes (display, root_return, &attrs))
667         return NULL;
668 
669       surface = cairo_xlib_surface_create (display,
670                                            xpixmap,
671                                            attrs.visual,
672                                            w_ret, h_ret);
673     }
674 
675   retval = gdk_pixbuf_get_from_surface (surface,
676                                         src_x,
677                                         src_y,
678                                         width,
679                                         height);
680   cairo_surface_destroy (surface);
681 
682   return retval;
683 }
684 
685 static GdkPixbuf *
load_default_window_icon(int size)686 load_default_window_icon (int size)
687 {
688   GtkIconTheme *theme = gtk_icon_theme_get_default ();
689   const char *icon_name;
690 
691   if (gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME))
692     icon_name = META_DEFAULT_ICON_NAME;
693   else
694     icon_name = "image-missing";
695 
696   return gtk_icon_theme_load_icon (theme, icon_name, size, 0, NULL);
697 }
698 
699 GdkPixbuf*
meta_ui_get_default_window_icon(MetaUI * ui,int ideal_size)700 meta_ui_get_default_window_icon (MetaUI *ui,
701                                  int     ideal_size)
702 {
703   static GdkPixbuf *default_icon = NULL;
704 
705   if (default_icon == NULL)
706     {
707       default_icon = load_default_window_icon (ideal_size);
708       g_assert (default_icon);
709     }
710 
711   g_object_ref (G_OBJECT (default_icon));
712 
713   return default_icon;
714 }
715 
716 GdkPixbuf*
meta_ui_get_default_mini_icon(MetaUI * ui,int ideal_size)717 meta_ui_get_default_mini_icon (MetaUI *ui,
718                                int     ideal_size)
719 {
720   static GdkPixbuf *default_icon = NULL;
721 
722   if (default_icon == NULL)
723     {
724       default_icon = load_default_window_icon (ideal_size);
725       g_assert (default_icon);
726     }
727 
728   g_object_ref (G_OBJECT (default_icon));
729 
730   return default_icon;
731 }
732 
733 gboolean
meta_ui_window_should_not_cause_focus(Display * xdisplay,Window xwindow)734 meta_ui_window_should_not_cause_focus (Display *xdisplay,
735                                        Window   xwindow)
736 {
737   GdkWindow *window;
738   GdkDisplay *display;
739 
740   display = gdk_x11_lookup_xdisplay (xdisplay);
741   window = gdk_x11_window_lookup_for_display (display, xwindow);
742 
743   /* we shouldn't cause focus if we're an override redirect
744    * toplevel which is not foreign
745    */
746   if (window && gdk_window_get_window_type (window) == GDK_WINDOW_TEMP)
747     return TRUE;
748   else
749     return FALSE;
750 }
751 
752 void
meta_ui_theme_get_frame_borders(MetaUI * ui,MetaFrameType type,MetaFrameFlags flags,MetaFrameBorders * borders)753 meta_ui_theme_get_frame_borders (MetaUI           *ui,
754                                  MetaFrameType     type,
755                                  MetaFrameFlags    flags,
756                                  MetaFrameBorders *borders)
757 {
758   MetaTheme *theme;
759   const gchar *theme_variant;
760 
761   theme = meta_ui_get_theme (ui);
762   theme_variant = NULL;
763 
764   meta_theme_get_frame_borders (theme, theme_variant, type, flags, borders);
765 }
766 
767 MetaTheme *
meta_ui_get_theme(MetaUI * ui)768 meta_ui_get_theme (MetaUI *ui)
769 {
770   return ui->theme;
771 }
772 
773 gboolean
meta_ui_is_composited(MetaUI * ui)774 meta_ui_is_composited (MetaUI *ui)
775 {
776   return ui->composited;
777 }
778 
779 static gchar *
get_theme_name(MetaThemeType theme_type)780 get_theme_name (MetaThemeType theme_type)
781 {
782   gchar *theme_name;
783 
784   if (theme_type == META_THEME_TYPE_METACITY)
785     {
786       theme_name = g_strdup (meta_prefs_get_theme_name ());
787     }
788   else
789     {
790       GtkSettings *settings;
791 
792       settings = gtk_settings_get_default ();
793 
794       g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
795     }
796 
797   return theme_name;
798 }
799 
800 static MetaTheme *
load_theme(MetaUI * ui,MetaThemeType theme_type,const gchar * theme_name)801 load_theme (MetaUI        *ui,
802             MetaThemeType  theme_type,
803             const gchar   *theme_name)
804 {
805   MetaTheme *theme;
806   const PangoFontDescription *titlebar_font;
807   GError *error;
808   const gchar *button_layout;
809   gboolean invert;
810 
811   theme = meta_theme_new (theme_type);
812 
813   meta_theme_set_composited (theme, ui->composited);
814   meta_theme_set_scale (theme, ui->scale);
815   meta_theme_set_dpi (theme, ui->dpi);
816 
817   titlebar_font = meta_prefs_get_titlebar_font ();
818   meta_theme_set_titlebar_font (theme, titlebar_font);
819 
820   error = NULL;
821   if (!meta_theme_load (theme, theme_name, &error))
822     {
823       g_warning ("%s", error->message);
824       g_error_free (error);
825 
826       g_object_unref (theme);
827       return NULL;
828     }
829 
830   button_layout = meta_prefs_get_button_layout ();
831   invert = meta_ui_get_direction() == META_UI_DIRECTION_RTL;
832 
833   meta_theme_set_button_layout (theme, button_layout, invert);
834 
835   return theme;
836 }
837 
838 void
meta_ui_reload_theme(MetaUI * ui)839 meta_ui_reload_theme (MetaUI *ui)
840 {
841   MetaThemeType theme_type;
842   gchar *theme_name;
843   MetaTheme *theme;
844 
845   theme_type = meta_prefs_get_theme_type ();
846   theme_name = get_theme_name (theme_type);
847 
848   theme = load_theme (ui, theme_type, theme_name);
849   g_free (theme_name);
850 
851   if (theme == NULL)
852     {
853       g_warning (_("Falling back to default GTK+ theme - Adwaita"));
854       theme = load_theme (ui, META_THEME_TYPE_GTK, "Adwaita");
855     }
856 
857   g_assert (theme);
858   g_clear_object (&ui->theme);
859   ui->theme = theme;
860 
861   meta_invalidate_default_icons ();
862 }
863 
864 void
meta_ui_update_button_layout(MetaUI * ui)865 meta_ui_update_button_layout (MetaUI *ui)
866 {
867   const gchar *button_layout;
868   gboolean invert;
869 
870   button_layout = meta_prefs_get_button_layout ();
871   invert = meta_ui_get_direction() == META_UI_DIRECTION_RTL;
872 
873   meta_theme_set_button_layout (ui->theme, button_layout, invert);
874 }
875 
876 static void
meta_ui_accelerator_parse(const char * accel,guint * keysym,guint * keycode,GdkModifierType * keymask)877 meta_ui_accelerator_parse (const char      *accel,
878                            guint           *keysym,
879                            guint           *keycode,
880                            GdkModifierType *keymask)
881 {
882   const char *above_tab;
883 
884   if (accel[0] == '0' && accel[1] == 'x')
885     {
886       *keysym = 0;
887       *keycode = (guint) strtoul (accel, NULL, 16);
888       *keymask = 0;
889 
890       return;
891     }
892 
893   /* The key name 'Above_Tab' is special - it's not an actual keysym name,
894    * but rather refers to the key above the tab key. In order to use
895    * the GDK parsing for modifiers in combination with it, we substitute
896    * it with 'Tab' temporarily before calling gtk_accelerator_parse().
897    */
898 #define is_word_character(c) (g_ascii_isalnum(c) || ((c) == '_'))
899 #define ABOVE_TAB "Above_Tab"
900 #define ABOVE_TAB_LEN 9
901 
902   above_tab = strstr (accel, ABOVE_TAB);
903   if (above_tab &&
904       (above_tab == accel || !is_word_character (above_tab[-1])) &&
905       !is_word_character (above_tab[ABOVE_TAB_LEN]))
906     {
907       char *before = g_strndup (accel, above_tab - accel);
908       char *after = g_strdup (above_tab + ABOVE_TAB_LEN);
909       char *replaced = g_strconcat (before, "Tab", after, NULL);
910 
911       gtk_accelerator_parse (replaced, NULL, keymask);
912 
913       g_free (before);
914       g_free (after);
915       g_free (replaced);
916 
917       *keysym = META_KEY_ABOVE_TAB;
918       return;
919     }
920 
921 #undef is_word_character
922 #undef ABOVE_TAB
923 #undef ABOVE_TAB_LEN
924 
925   gtk_accelerator_parse (accel, keysym, keymask);
926 }
927 
928 gboolean
meta_ui_parse_accelerator(const char * accel,unsigned int * keysym,unsigned int * keycode,MetaVirtualModifier * mask)929 meta_ui_parse_accelerator (const char          *accel,
930                            unsigned int        *keysym,
931                            unsigned int        *keycode,
932                            MetaVirtualModifier *mask)
933 {
934   GdkModifierType gdk_mask = 0;
935   guint gdk_sym = 0;
936   guint gdk_code = 0;
937 
938   *keysym = 0;
939   *keycode = 0;
940   *mask = 0;
941 
942   if (!accel[0] || strcmp (accel, "disabled") == 0)
943     return TRUE;
944 
945   meta_ui_accelerator_parse (accel, &gdk_sym, &gdk_code, &gdk_mask);
946   if (gdk_mask == 0 && gdk_sym == 0 && gdk_code == 0)
947     return FALSE;
948 
949   if (gdk_sym == None && gdk_code == 0)
950     return FALSE;
951 
952   if (gdk_mask & GDK_RELEASE_MASK) /* we don't allow this */
953     return FALSE;
954 
955   *keysym = gdk_sym;
956   *keycode = gdk_code;
957 
958   if (gdk_mask & GDK_SHIFT_MASK)
959     *mask |= META_VIRTUAL_SHIFT_MASK;
960   if (gdk_mask & GDK_CONTROL_MASK)
961     *mask |= META_VIRTUAL_CONTROL_MASK;
962   if (gdk_mask & GDK_MOD1_MASK)
963     *mask |= META_VIRTUAL_ALT_MASK;
964   if (gdk_mask & GDK_MOD2_MASK)
965     *mask |= META_VIRTUAL_MOD2_MASK;
966   if (gdk_mask & GDK_MOD3_MASK)
967     *mask |= META_VIRTUAL_MOD3_MASK;
968   if (gdk_mask & GDK_MOD4_MASK)
969     *mask |= META_VIRTUAL_MOD4_MASK;
970   if (gdk_mask & GDK_MOD5_MASK)
971     *mask |= META_VIRTUAL_MOD5_MASK;
972   if (gdk_mask & GDK_SUPER_MASK)
973     *mask |= META_VIRTUAL_SUPER_MASK;
974   if (gdk_mask & GDK_HYPER_MASK)
975     *mask |= META_VIRTUAL_HYPER_MASK;
976   if (gdk_mask & GDK_META_MASK)
977     *mask |= META_VIRTUAL_META_MASK;
978 
979   return TRUE;
980 }
981 
982 gboolean
meta_ui_parse_modifier(const char * accel,MetaVirtualModifier * mask)983 meta_ui_parse_modifier (const char          *accel,
984                         MetaVirtualModifier *mask)
985 {
986   GdkModifierType gdk_mask = 0;
987   guint gdk_sym = 0;
988   guint gdk_code = 0;
989 
990   *mask = 0;
991 
992   if (accel == NULL || !accel[0] || strcmp (accel, "disabled") == 0)
993     return TRUE;
994 
995   meta_ui_accelerator_parse (accel, &gdk_sym, &gdk_code, &gdk_mask);
996   if (gdk_mask == 0 && gdk_sym == 0 && gdk_code == 0)
997     return FALSE;
998 
999   if (gdk_sym != None || gdk_code != 0)
1000     return FALSE;
1001 
1002   if (gdk_mask & GDK_RELEASE_MASK) /* we don't allow this */
1003     return FALSE;
1004 
1005   if (gdk_mask & GDK_SHIFT_MASK)
1006     *mask |= META_VIRTUAL_SHIFT_MASK;
1007   if (gdk_mask & GDK_CONTROL_MASK)
1008     *mask |= META_VIRTUAL_CONTROL_MASK;
1009   if (gdk_mask & GDK_MOD1_MASK)
1010     *mask |= META_VIRTUAL_ALT_MASK;
1011   if (gdk_mask & GDK_MOD2_MASK)
1012     *mask |= META_VIRTUAL_MOD2_MASK;
1013   if (gdk_mask & GDK_MOD3_MASK)
1014     *mask |= META_VIRTUAL_MOD3_MASK;
1015   if (gdk_mask & GDK_MOD4_MASK)
1016     *mask |= META_VIRTUAL_MOD4_MASK;
1017   if (gdk_mask & GDK_MOD5_MASK)
1018     *mask |= META_VIRTUAL_MOD5_MASK;
1019   if (gdk_mask & GDK_SUPER_MASK)
1020     *mask |= META_VIRTUAL_SUPER_MASK;
1021   if (gdk_mask & GDK_HYPER_MASK)
1022     *mask |= META_VIRTUAL_HYPER_MASK;
1023   if (gdk_mask & GDK_META_MASK)
1024     *mask |= META_VIRTUAL_META_MASK;
1025 
1026   return TRUE;
1027 }
1028 
1029 gboolean
meta_ui_window_is_widget(MetaUI * ui,Window xwindow)1030 meta_ui_window_is_widget (MetaUI *ui,
1031                           Window  xwindow)
1032 {
1033   GdkDisplay *display;
1034   GdkWindow *window;
1035 
1036   display = gdk_x11_lookup_xdisplay (ui->xdisplay);
1037   window = gdk_x11_window_lookup_for_display (display, xwindow);
1038 
1039   if (window)
1040     {
1041       void *user_data = NULL;
1042       gdk_window_get_user_data (window, &user_data);
1043       return user_data != NULL && user_data != ui->frames;
1044     }
1045   else
1046     return FALSE;
1047 }
1048 
1049 int
meta_ui_get_drag_threshold(MetaUI * ui)1050 meta_ui_get_drag_threshold (MetaUI *ui)
1051 {
1052   GtkSettings *settings;
1053   int threshold;
1054 
1055   settings = gtk_widget_get_settings (GTK_WIDGET (ui->frames));
1056 
1057   threshold = 8;
1058   g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &threshold, NULL);
1059 
1060   return threshold;
1061 }
1062 
1063 MetaUIDirection
meta_ui_get_direction(void)1064 meta_ui_get_direction (void)
1065 {
1066   if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL)
1067     return META_UI_DIRECTION_RTL;
1068 
1069   return META_UI_DIRECTION_LTR;
1070 }
1071 
1072 GdkPixbuf *
meta_ui_get_pixbuf_from_surface(cairo_surface_t * surface)1073 meta_ui_get_pixbuf_from_surface (cairo_surface_t *surface)
1074 {
1075   gint width;
1076   gint height;
1077 
1078   width = cairo_xlib_surface_get_width (surface);
1079   height = cairo_xlib_surface_get_height (surface);
1080 
1081   return gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
1082 }
1083