1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Muffin X window decorations */
4 
5 /*
6  * Copyright (C) 2001 Havoc Pennington
7  * Copyright (C) 2003, 2004 Red Hat, Inc.
8  * Copyright (C) 2005 Elijah Newren
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
23  * 02110-1335, USA.
24  */
25 
26 #include <config.h>
27 #include "frame.h"
28 #include <meta/errors.h>
29 #include "keybindings-private.h"
30 
31 #include <X11/extensions/Xrender.h>
32 
33 #define EVENT_MASK (SubstructureRedirectMask |                     \
34                     StructureNotifyMask | SubstructureNotifyMask | \
35                     ExposureMask |                                 \
36                     ButtonPressMask | ButtonReleaseMask |          \
37                     PointerMotionMask | PointerMotionHintMask |    \
38                     EnterWindowMask | LeaveWindowMask |            \
39                     FocusChangeMask |                              \
40                     ColormapChangeMask)
41 
42 LOCAL_SYMBOL void
meta_window_ensure_frame(MetaWindow * window)43 meta_window_ensure_frame (MetaWindow *window)
44 {
45   MetaFrame *frame;
46   XSetWindowAttributes attrs;
47   Visual *visual;
48   gulong create_serial;
49 
50   if (window->frame)
51     return;
52 
53   /* See comment below for why this is required. */
54   meta_display_grab (window->display);
55 
56   frame = g_new (MetaFrame, 1);
57 
58   frame->window = window;
59   frame->xwindow = None;
60 
61   frame->rect = window->rect;
62   frame->child_x = 0;
63   frame->child_y = 0;
64   frame->bottom_height = 0;
65   frame->right_width = 0;
66   frame->current_cursor = 0;
67 
68   frame->is_flashing = FALSE;
69   frame->borders_cached = FALSE;
70 
71   meta_verbose ("Framing window %s: visual %s default, depth %d default depth %d\n",
72                 window->desc,
73                 XVisualIDFromVisual (window->xvisual) ==
74                 XVisualIDFromVisual (window->screen->default_xvisual) ?
75                 "is" : "is not",
76                 window->depth, window->screen->default_depth);
77   meta_verbose ("Frame geometry %d,%d  %dx%d\n",
78                 frame->rect.x, frame->rect.y,
79                 frame->rect.width, frame->rect.height);
80 
81   /* Default depth/visual handles clients with weird visuals; they can
82    * always be children of the root depth/visual obviously, but
83    * e.g. DRI games can't be children of a parent that has the same
84    * visual as the client. NULL means default visual.
85    *
86    * We look for an ARGB visual if we can find one, otherwise use
87    * the default of NULL.
88    */
89 
90   /* Special case for depth 32 windows (assumed to be ARGB),
91    * we use the window's visual. Otherwise we just use the system visual.
92    */
93   if (window->depth == 32)
94     visual = window->xvisual;
95   else
96     visual = NULL;
97 
98   frame->xwindow = meta_ui_create_frame_window (window->screen->ui,
99                                                 window->display->xdisplay,
100                                                 visual,
101                                                 frame->rect.x,
102                                                 frame->rect.y,
103 						frame->rect.width,
104 						frame->rect.height,
105 						frame->window->screen->number,
106                                                 &create_serial);
107   meta_stack_tracker_record_add (window->screen->stack_tracker,
108                                  frame->xwindow,
109                                  create_serial);
110 
111   meta_verbose ("Frame for %s is 0x%lx\n", frame->window->desc, frame->xwindow);
112   attrs.event_mask = EVENT_MASK;
113   XChangeWindowAttributes (window->display->xdisplay,
114 			   frame->xwindow, CWEventMask, &attrs);
115 
116   meta_display_register_x_window (window->display, &frame->xwindow, window);
117 
118   /* Reparent the client window; it may be destroyed,
119    * thus the error trap. We'll get a destroy notify later
120    * and free everything. Comment in FVWM source code says
121    * we need a server grab or the child can get its MapNotify
122    * before we've finished reparenting and getting the decoration
123    * window onscreen, so ensure_frame must be called with
124    * a grab.
125    */
126   meta_error_trap_push (window->display);
127   if (window->mapped)
128     {
129       window->mapped = FALSE; /* the reparent will unmap the window,
130                                * we don't want to take that as a withdraw
131                                */
132       meta_topic (META_DEBUG_WINDOW_STATE,
133                   "Incrementing unmaps_pending on %s for reparent\n", window->desc);
134       window->unmaps_pending += 1;
135     }
136   /* window was reparented to this position */
137   window->rect.x = 0;
138   window->rect.y = 0;
139 
140   meta_stack_tracker_record_remove (window->screen->stack_tracker,
141                                     window->xwindow,
142                                     XNextRequest (window->display->xdisplay));
143   XReparentWindow (window->display->xdisplay,
144                    window->xwindow,
145                    frame->xwindow,
146                    window->rect.x,
147                    window->rect.y);
148   /* FIXME handle this error */
149   meta_error_trap_pop (window->display);
150 
151   /* stick frame to the window */
152   window->frame = frame;
153 
154   /* Now that frame->xwindow is registered with window, we can set its
155    * style and background.
156    */
157   meta_ui_update_frame_style (window->screen->ui, frame->xwindow);
158 
159   if (window->title)
160     meta_ui_set_frame_title (window->screen->ui,
161                              window->frame->xwindow,
162                              window->title);
163 
164   /* Move keybindings to frame instead of window */
165   meta_window_grab_keys (window);
166 
167   meta_ui_map_frame (frame->window->screen->ui, frame->xwindow);
168 
169   meta_display_ungrab (window->display);
170 }
171 
172 LOCAL_SYMBOL void
meta_window_destroy_frame(MetaWindow * window)173 meta_window_destroy_frame (MetaWindow *window)
174 {
175   MetaFrame *frame;
176   MetaFrameBorders borders;
177 
178   if (window->frame == NULL)
179     return;
180 
181   meta_verbose ("Unframing window %s\n", window->desc);
182 
183   frame = window->frame;
184 
185   meta_frame_calc_borders (frame, &borders);
186 
187   /* Unparent the client window; it may be destroyed,
188    * thus the error trap.
189    */
190   meta_error_trap_push (window->display);
191   if (window->mapped)
192     {
193       window->mapped = FALSE; /* Keep track of unmapping it, so we
194                                * can identify a withdraw initiated
195                                * by the client.
196                                */
197       meta_topic (META_DEBUG_WINDOW_STATE,
198                   "Incrementing unmaps_pending on %s for reparent back to root\n", window->desc);
199       window->unmaps_pending += 1;
200     }
201   meta_stack_tracker_record_add (window->screen->stack_tracker,
202                                  window->xwindow,
203                                  XNextRequest (window->display->xdisplay));
204   XReparentWindow (window->display->xdisplay,
205                    window->xwindow,
206                    window->screen->xroot,
207                    /* Using anything other than meta_window_get_position()
208                     * coordinates here means we'll need to ensure a configure
209                     * notify event is sent; see bug 399552.
210                     */
211                    window->frame->rect.x + borders.total.left,
212                    window->frame->rect.y + borders.total.top);
213 
214   meta_error_trap_pop (window->display);
215 
216   meta_ui_destroy_frame_window (window->screen->ui, frame->xwindow);
217 
218   meta_display_unregister_x_window (window->display,
219                                     frame->xwindow);
220 
221   window->frame = NULL;
222   if (window->frame_bounds)
223     {
224       cairo_region_destroy (window->frame_bounds);
225       window->frame_bounds = NULL;
226     }
227 
228   /* Move keybindings to window instead of frame */
229   meta_window_grab_keys (window);
230 
231   free (frame);
232 
233   /* Put our state back where it should be */
234   meta_window_queue (window, META_QUEUE_CALC_SHOWING);
235   meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
236 }
237 
238 
239 LOCAL_SYMBOL MetaFrameFlags
meta_frame_get_flags(MetaFrame * frame)240 meta_frame_get_flags (MetaFrame *frame)
241 {
242   MetaFrameFlags flags;
243 
244   flags = 0;
245 
246   if (frame->window->border_only)
247     {
248       ; /* FIXME this may disable the _function_ as well as decor
249          * in some cases, which is sort of wrong.
250          */
251     }
252   else
253     {
254       flags |= META_FRAME_ALLOWS_MENU;
255 
256       if (frame->window->has_close_func)
257         flags |= META_FRAME_ALLOWS_DELETE;
258 
259       if (frame->window->has_maximize_func)
260         flags |= META_FRAME_ALLOWS_MAXIMIZE;
261 
262       if (frame->window->has_minimize_func)
263         flags |= META_FRAME_ALLOWS_MINIMIZE;
264 
265       if (frame->window->has_shade_func)
266         flags |= META_FRAME_ALLOWS_SHADE;
267     }
268 
269   if (META_WINDOW_ALLOWS_MOVE (frame->window))
270     flags |= META_FRAME_ALLOWS_MOVE;
271 
272   if (META_WINDOW_ALLOWS_HORIZONTAL_RESIZE (frame->window))
273     flags |= META_FRAME_ALLOWS_HORIZONTAL_RESIZE;
274 
275   if (META_WINDOW_ALLOWS_VERTICAL_RESIZE (frame->window))
276     flags |= META_FRAME_ALLOWS_VERTICAL_RESIZE;
277 
278   if (META_WINDOW_ALLOWS_TOP_RESIZE (frame->window))
279     flags |= META_FRAME_ALLOWS_TOP_RESIZE;
280 
281   if (META_WINDOW_ALLOWS_BOTTOM_RESIZE (frame->window))
282     flags |= META_FRAME_ALLOWS_BOTTOM_RESIZE;
283 
284   if (META_WINDOW_ALLOWS_LEFT_RESIZE (frame->window))
285     flags |= META_FRAME_ALLOWS_LEFT_RESIZE;
286 
287   if (META_WINDOW_ALLOWS_RIGHT_RESIZE (frame->window))
288     flags |= META_FRAME_ALLOWS_RIGHT_RESIZE;
289 
290   if (meta_window_appears_focused (frame->window))
291     flags |= META_FRAME_HAS_FOCUS;
292 
293   if (frame->window->shaded)
294     flags |= META_FRAME_SHADED;
295 
296   if (frame->window->on_all_workspaces_requested)
297     flags |= META_FRAME_STUCK;
298 
299   /* FIXME: Should we have some kind of UI for windows that are just vertically
300    * maximized or just horizontally maximized?
301    */
302   if (META_WINDOW_MAXIMIZED (frame->window))
303     flags |= META_FRAME_MAXIMIZED;
304 
305   if (META_WINDOW_TILED_LEFT (frame->window) ||
306       META_WINDOW_TILED_ULC (frame->window) ||
307       META_WINDOW_TILED_LLC (frame->window))
308     flags |= META_FRAME_TILED_LEFT;
309 
310   if (META_WINDOW_TILED_RIGHT (frame->window) ||
311       META_WINDOW_TILED_URC (frame->window) ||
312       META_WINDOW_TILED_LRC (frame->window))
313     flags |= META_FRAME_TILED_RIGHT;
314 
315   if (META_WINDOW_TILED_TOP (frame->window) ||
316       META_WINDOW_TILED_BOTTOM (frame->window)) {
317     flags |= META_FRAME_MAXIMIZED;
318   }
319 
320   if (frame->window->fullscreen)
321     flags |= META_FRAME_FULLSCREEN;
322 
323   if (frame->is_flashing)
324     flags |= META_FRAME_IS_FLASHING;
325 
326   if (frame->window->wm_state_above)
327     flags |= META_FRAME_ABOVE;
328 
329   return flags;
330 }
331 
332 void
meta_frame_borders_clear(MetaFrameBorders * self)333 meta_frame_borders_clear (MetaFrameBorders *self)
334 {
335   self->visible.top    = self->invisible.top    = self->total.top    = 0;
336   self->visible.bottom = self->invisible.bottom = self->total.bottom = 0;
337   self->visible.left   = self->invisible.left   = self->total.left   = 0;
338   self->visible.right  = self->invisible.right  = self->total.right  = 0;
339 }
340 
341 LOCAL_SYMBOL void
meta_frame_calc_borders(MetaFrame * frame,MetaFrameBorders * borders)342 meta_frame_calc_borders (MetaFrame        *frame,
343                          MetaFrameBorders *borders)
344 {
345   /* Save on if statements and potential uninitialized values
346    * in callers -- if there's no frame, then zero the borders. */
347   if (frame == NULL)
348     meta_frame_borders_clear (borders);
349   else
350     {
351       if (!frame->borders_cached)
352         {
353           meta_ui_get_frame_borders (frame->window->screen->ui,
354                                      frame->xwindow,
355                                      &frame->cached_borders);
356           frame->borders_cached = TRUE;
357         }
358 
359       *borders = frame->cached_borders;
360     }
361 }
362 
363 void
meta_frame_clear_cached_borders(MetaFrame * frame)364 meta_frame_clear_cached_borders (MetaFrame *frame)
365 {
366   frame->borders_cached = FALSE;
367 }
368 
369 LOCAL_SYMBOL void
meta_frame_get_corner_radiuses(MetaFrame * frame,float * top_left,float * top_right,float * bottom_left,float * bottom_right)370 meta_frame_get_corner_radiuses (MetaFrame *frame,
371                                 float     *top_left,
372                                 float     *top_right,
373                                 float     *bottom_left,
374                                 float     *bottom_right)
375 {
376   meta_ui_get_corner_radiuses (frame->window->screen->ui,
377                                frame->xwindow,
378                                top_left, top_right,
379                                bottom_left, bottom_right);
380 }
381 
382 LOCAL_SYMBOL gboolean
meta_frame_sync_to_window(MetaFrame * frame,int resize_gravity,gboolean need_move,gboolean need_resize)383 meta_frame_sync_to_window (MetaFrame *frame,
384                            int        resize_gravity,
385                            gboolean   need_move,
386                            gboolean   need_resize)
387 {
388   meta_topic (META_DEBUG_GEOMETRY,
389               "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)\n",
390               frame->rect.x, frame->rect.y,
391               frame->rect.width, frame->rect.height,
392               frame->rect.x + frame->rect.width,
393               frame->rect.y + frame->rect.height);
394 
395   meta_ui_move_resize_frame (frame->window->screen->ui,
396 			     frame->xwindow,
397 			     frame->rect.x,
398 			     frame->rect.y,
399 			     frame->rect.width,
400 			     frame->rect.height);
401 
402   if (need_resize)
403     {
404       /* If we're interactively resizing the frame, repaint
405        * it immediately so we don't start to lag.
406        */
407       if (frame->window->display->grab_window ==
408           frame->window)
409         meta_ui_repaint_frame (frame->window->screen->ui,
410                                frame->xwindow);
411     }
412 
413   return need_resize;
414 }
415 
416 LOCAL_SYMBOL cairo_region_t *
meta_frame_get_frame_bounds(MetaFrame * frame)417 meta_frame_get_frame_bounds (MetaFrame *frame)
418 {
419   return meta_ui_get_frame_bounds (frame->window->screen->ui,
420                                    frame->xwindow,
421                                    frame->rect.width,
422                                    frame->rect.height);
423 }
424 
425 LOCAL_SYMBOL void
meta_frame_queue_draw(MetaFrame * frame)426 meta_frame_queue_draw (MetaFrame *frame)
427 {
428   meta_ui_queue_frame_draw (frame->window->screen->ui,
429                             frame->xwindow);
430 }
431 
432 LOCAL_SYMBOL void
meta_frame_set_screen_cursor(MetaFrame * frame,MetaCursor cursor)433 meta_frame_set_screen_cursor (MetaFrame	*frame,
434 			      MetaCursor cursor)
435 {
436   Cursor xcursor;
437   if (cursor == frame->current_cursor)
438     return;
439   frame->current_cursor = cursor;
440   if (cursor == META_CURSOR_DEFAULT)
441     XUndefineCursor (frame->window->display->xdisplay, frame->xwindow);
442   else
443     {
444       xcursor = meta_display_create_x_cursor (frame->window->display, cursor);
445       XDefineCursor (frame->window->display->xdisplay, frame->xwindow, xcursor);
446       XFlush (frame->window->display->xdisplay);
447       XFreeCursor (frame->window->display->xdisplay, xcursor);
448     }
449 }
450 
451 LOCAL_SYMBOL Window
meta_frame_get_xwindow(MetaFrame * frame)452 meta_frame_get_xwindow (MetaFrame *frame)
453 {
454   return frame->xwindow;
455 }
456