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