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