]> icculus.org git repositories - dana/openbox.git/blob - engines/openbox/obengine.c
use a context enum instead of quarks
[dana/openbox.git] / engines / openbox / obengine.c
1 #include "obtheme.h"
2 #include "obrender.h"
3 #include "obengine.h"
4 #include "../../kernel/openbox.h"
5 #include "../../kernel/extensions.h"
6 #include "../../kernel/dispatch.h"
7 #include "../../kernel/config.h"
8
9 #ifdef HAVE_SYS_STAT_H
10 #  include <sys/stat.h>
11 #  include <sys/types.h>
12 #endif
13 #include <X11/Xlib.h>
14 #include <glib.h>
15
16 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
17 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
18                          ButtonPressMask | ButtonReleaseMask)
19 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
20                            ButtonMotionMask | ExposureMask)
21
22 /* style settings - geometry */
23 int ob_s_bevel;
24 int ob_s_handle_height;
25 int ob_s_bwidth;
26 int ob_s_cbwidth;
27 /* style settings - colors */
28 color_rgb *ob_s_b_color;
29 color_rgb *ob_s_cb_focused_color;
30 color_rgb *ob_s_cb_unfocused_color;
31 color_rgb *ob_s_title_focused_color;
32 color_rgb *ob_s_title_unfocused_color;
33 color_rgb *ob_s_titlebut_focused_color;
34 color_rgb *ob_s_titlebut_unfocused_color;
35 /* style settings - fonts */
36 int ob_s_winfont_height;
37 int ob_s_winfont_shadow;
38 int ob_s_winfont_shadow_offset;
39 int ob_s_winfont_shadow_tint;
40 ObFont *ob_s_winfont;
41 /* style settings - masks */
42 pixmap_mask *ob_s_max_set_mask;
43 pixmap_mask *ob_s_max_unset_mask;
44 pixmap_mask *ob_s_iconify_mask;
45 pixmap_mask *ob_s_desk_set_mask;
46 pixmap_mask *ob_s_desk_unset_mask;
47 pixmap_mask *ob_s_shade_set_mask;
48 pixmap_mask *ob_s_shade_unset_mask;
49 pixmap_mask *ob_s_close_mask;
50
51 /* global appearances */
52 Appearance *ob_a_focused_unpressed_max;
53 Appearance *ob_a_focused_pressed_max;
54 Appearance *ob_a_focused_pressed_set_max;
55 Appearance *ob_a_unfocused_unpressed_max;
56 Appearance *ob_a_unfocused_pressed_max;
57 Appearance *ob_a_unfocused_pressed_set_max;
58 Appearance *ob_a_focused_unpressed_close;
59 Appearance *ob_a_focused_pressed_close;
60 Appearance *ob_a_unfocused_unpressed_close;
61 Appearance *ob_a_unfocused_pressed_close;
62 Appearance *ob_a_focused_unpressed_desk;
63 Appearance *ob_a_focused_pressed_desk;
64 Appearance *ob_a_focused_pressed_set_desk;
65 Appearance *ob_a_unfocused_unpressed_desk;
66 Appearance *ob_a_unfocused_pressed_desk;
67 Appearance *ob_a_unfocused_pressed_set_desk;
68 Appearance *ob_a_focused_unpressed_shade;
69 Appearance *ob_a_focused_pressed_shade;
70 Appearance *ob_a_focused_pressed_set_shade;
71 Appearance *ob_a_unfocused_unpressed_shade;
72 Appearance *ob_a_unfocused_pressed_shade;
73 Appearance *ob_a_unfocused_pressed_set_shade;
74 Appearance *ob_a_focused_unpressed_iconify;
75 Appearance *ob_a_focused_pressed_iconify;
76 Appearance *ob_a_unfocused_unpressed_iconify;
77 Appearance *ob_a_unfocused_pressed_iconify;
78 Appearance *ob_a_focused_grip;
79 Appearance *ob_a_unfocused_grip;
80 Appearance *ob_a_focused_title;
81 Appearance *ob_a_unfocused_title;
82 Appearance *ob_a_focused_label;
83 Appearance *ob_a_unfocused_label;
84 Appearance *ob_a_icon; /* always parentrelative, so no focused/unfocused */
85 Appearance *ob_a_focused_handle;
86 Appearance *ob_a_unfocused_handle;
87
88 static void layout_title(ObFrame *self);
89 static void mouse_event(const ObEvent *e, ObFrame *self);
90
91 gboolean startup()
92 {
93     char *path;
94
95     /* create the ~/.openbox/themes/openbox dir */
96     path = g_build_filename(g_get_home_dir(), ".openbox", "themes", "openbox",
97                             NULL);
98     mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
99                  S_IROTH | S_IWOTH | S_IXOTH));
100     g_free(path);
101
102     ob_s_b_color = ob_s_cb_unfocused_color = ob_s_cb_focused_color = 
103         ob_s_title_unfocused_color = ob_s_title_focused_color = 
104         ob_s_titlebut_unfocused_color = ob_s_titlebut_focused_color = NULL;
105     ob_s_winfont = NULL;
106     ob_s_max_set_mask = ob_s_max_unset_mask = NULL;
107     ob_s_desk_set_mask = ob_s_desk_unset_mask = NULL;
108     ob_s_shade_set_mask = ob_s_shade_unset_mask = NULL;
109     ob_s_iconify_mask = ob_s_close_mask = NULL;
110
111     ob_a_focused_unpressed_max = appearance_new(Surface_Planar, 1);
112     ob_a_focused_pressed_max = appearance_new(Surface_Planar, 1);
113     ob_a_focused_pressed_set_max = appearance_new(Surface_Planar, 1);
114     ob_a_unfocused_unpressed_max = appearance_new(Surface_Planar, 1);
115     ob_a_unfocused_pressed_max = appearance_new(Surface_Planar, 1);
116     ob_a_unfocused_pressed_set_max = appearance_new(Surface_Planar, 1);
117     ob_a_focused_unpressed_close = NULL;
118     ob_a_focused_pressed_close = NULL;
119     ob_a_unfocused_unpressed_close = NULL;
120     ob_a_unfocused_pressed_close = NULL;
121     ob_a_focused_unpressed_desk = NULL;
122     ob_a_focused_pressed_desk = NULL;
123     ob_a_focused_pressed_set_desk = NULL;
124     ob_a_unfocused_unpressed_desk = NULL;
125     ob_a_unfocused_pressed_desk = NULL;
126     ob_a_unfocused_pressed_set_desk = NULL;
127     ob_a_focused_unpressed_shade = NULL;
128     ob_a_focused_pressed_shade = NULL;
129     ob_a_focused_pressed_set_shade = NULL;
130     ob_a_unfocused_unpressed_shade = NULL;
131     ob_a_unfocused_pressed_shade = NULL;
132     ob_a_unfocused_pressed_set_shade = NULL;
133     ob_a_focused_unpressed_iconify = NULL;
134     ob_a_focused_pressed_iconify = NULL;
135     ob_a_unfocused_unpressed_iconify = NULL;
136     ob_a_unfocused_pressed_iconify = NULL;
137     ob_a_focused_grip = appearance_new(Surface_Planar, 0);
138     ob_a_unfocused_grip = appearance_new(Surface_Planar, 0);
139     ob_a_focused_title = appearance_new(Surface_Planar, 0);
140     ob_a_unfocused_title = appearance_new(Surface_Planar, 0);
141     ob_a_focused_label = appearance_new(Surface_Planar, 1);
142     ob_a_unfocused_label = appearance_new(Surface_Planar, 1);
143     ob_a_icon = appearance_new(Surface_Planar, 1);
144     ob_a_focused_handle = appearance_new(Surface_Planar, 0);
145     ob_a_unfocused_handle = appearance_new(Surface_Planar, 0);
146
147     if (obtheme_load()) {
148         RECT_SET(ob_a_focused_pressed_desk->area, 0, 0,
149                  BUTTON_SIZE, BUTTON_SIZE);
150         RECT_SET(ob_a_focused_pressed_set_desk->area, 0, 0,
151                  BUTTON_SIZE, BUTTON_SIZE);
152         RECT_SET(ob_a_focused_unpressed_desk->area, 0, 0,
153                  BUTTON_SIZE, BUTTON_SIZE);
154         RECT_SET(ob_a_unfocused_pressed_desk->area, 0, 0,
155                  BUTTON_SIZE, BUTTON_SIZE);
156         RECT_SET(ob_a_unfocused_pressed_set_desk->area, 0, 0,
157                  BUTTON_SIZE, BUTTON_SIZE);
158         RECT_SET(ob_a_unfocused_unpressed_desk->area, 0, 0,
159                  BUTTON_SIZE, BUTTON_SIZE);
160         RECT_SET(ob_a_focused_pressed_shade->area, 0, 0,
161                  BUTTON_SIZE, BUTTON_SIZE);
162         RECT_SET(ob_a_focused_pressed_set_shade->area, 0, 0,
163                  BUTTON_SIZE, BUTTON_SIZE);
164         RECT_SET(ob_a_focused_unpressed_shade->area, 0, 0,
165                  BUTTON_SIZE, BUTTON_SIZE);
166         RECT_SET(ob_a_unfocused_pressed_shade->area, 0, 0,
167                  BUTTON_SIZE, BUTTON_SIZE);
168         RECT_SET(ob_a_unfocused_pressed_set_shade->area, 0, 0,
169                  BUTTON_SIZE, BUTTON_SIZE);
170         RECT_SET(ob_a_unfocused_unpressed_shade->area, 0, 0,
171                  BUTTON_SIZE, BUTTON_SIZE);
172         RECT_SET(ob_a_focused_pressed_iconify->area, 0, 0,
173                  BUTTON_SIZE, BUTTON_SIZE);
174         RECT_SET(ob_a_focused_unpressed_iconify->area, 0, 0,
175                  BUTTON_SIZE, BUTTON_SIZE);
176         RECT_SET(ob_a_unfocused_pressed_iconify->area, 0, 0,
177                  BUTTON_SIZE, BUTTON_SIZE);
178         RECT_SET(ob_a_unfocused_unpressed_iconify->area, 0, 0,
179                  BUTTON_SIZE, BUTTON_SIZE);
180         RECT_SET(ob_a_unfocused_unpressed_iconify->area, 0, 0,
181                  BUTTON_SIZE, BUTTON_SIZE);
182         RECT_SET(ob_a_focused_pressed_max->area, 0, 0,
183                  BUTTON_SIZE, BUTTON_SIZE);
184         RECT_SET(ob_a_focused_pressed_set_max->area, 0, 0,
185                  BUTTON_SIZE, BUTTON_SIZE);
186         RECT_SET(ob_a_focused_unpressed_max->area, 0, 0,
187                  BUTTON_SIZE, BUTTON_SIZE);
188         RECT_SET(ob_a_unfocused_pressed_max->area, 0, 0,
189                  BUTTON_SIZE, BUTTON_SIZE);
190         RECT_SET(ob_a_unfocused_pressed_set_max->area, 0, 0,
191                  BUTTON_SIZE, BUTTON_SIZE);
192         RECT_SET(ob_a_unfocused_unpressed_max->area, 0, 0,
193                  BUTTON_SIZE, BUTTON_SIZE);
194         RECT_SET(ob_a_focused_pressed_close->area, 0, 0,
195                  BUTTON_SIZE, BUTTON_SIZE);
196         RECT_SET(ob_a_focused_unpressed_close->area, 0, 0,
197                  BUTTON_SIZE, BUTTON_SIZE);
198         RECT_SET(ob_a_unfocused_pressed_close->area, 0, 0,
199                  BUTTON_SIZE, BUTTON_SIZE);
200         RECT_SET(ob_a_unfocused_unpressed_close->area, 0, 0,
201                  BUTTON_SIZE, BUTTON_SIZE);
202
203         RECT_SET(ob_a_focused_grip->area, 0, 0,
204                  GRIP_WIDTH, ob_s_handle_height);
205         RECT_SET(ob_a_unfocused_grip->area, 0, 0,
206                  GRIP_WIDTH, ob_s_handle_height);
207         return TRUE;
208     } else
209         return FALSE;
210 }
211
212 void shutdown()
213 {
214     if (ob_s_b_color != NULL) color_free(ob_s_b_color);
215     if (ob_s_cb_unfocused_color != NULL) color_free(ob_s_cb_unfocused_color);
216     if (ob_s_cb_focused_color != NULL) color_free(ob_s_cb_focused_color);
217     if (ob_s_title_unfocused_color != NULL) color_free(ob_s_title_unfocused_color);
218     if (ob_s_title_focused_color != NULL) color_free(ob_s_title_focused_color);
219     if (ob_s_titlebut_unfocused_color != NULL)
220         color_free(ob_s_titlebut_unfocused_color);
221     if (ob_s_titlebut_focused_color != NULL)
222         color_free(ob_s_titlebut_focused_color);
223
224     if (ob_s_max_set_mask != NULL)
225         pixmap_mask_free(ob_s_max_set_mask);
226     if (ob_s_max_unset_mask != NULL)
227         pixmap_mask_free(ob_s_max_unset_mask);
228     if (ob_s_desk_set_mask != NULL)
229         pixmap_mask_free(ob_s_desk_set_mask);
230     if (ob_s_desk_unset_mask != NULL)
231         pixmap_mask_free(ob_s_desk_unset_mask);
232     if (ob_s_shade_set_mask != NULL)
233         pixmap_mask_free(ob_s_shade_set_mask);
234     if (ob_s_shade_unset_mask != NULL)
235         pixmap_mask_free(ob_s_shade_unset_mask);
236     if (ob_s_iconify_mask != NULL)
237         pixmap_mask_free(ob_s_iconify_mask);
238     if (ob_s_close_mask != NULL)
239         pixmap_mask_free(ob_s_close_mask);
240
241     if (ob_s_winfont != NULL) font_close(ob_s_winfont);
242
243     appearance_free(ob_a_focused_unpressed_max);
244     appearance_free(ob_a_focused_pressed_max);
245     appearance_free(ob_a_focused_pressed_set_max);
246     appearance_free(ob_a_unfocused_unpressed_max);
247     appearance_free(ob_a_unfocused_pressed_max);
248     appearance_free(ob_a_unfocused_pressed_set_max);
249     if (ob_a_focused_unpressed_close != NULL)
250         appearance_free(ob_a_focused_unpressed_close);
251     if (ob_a_focused_pressed_close != NULL)
252         appearance_free(ob_a_focused_pressed_close);
253     if (ob_a_unfocused_unpressed_close != NULL)
254         appearance_free(ob_a_unfocused_unpressed_close);
255     if (ob_a_unfocused_pressed_close != NULL)
256         appearance_free(ob_a_unfocused_pressed_close);
257     if (ob_a_focused_unpressed_desk != NULL)
258         appearance_free(ob_a_focused_unpressed_desk);
259     if (ob_a_focused_pressed_desk != NULL)
260         appearance_free(ob_a_focused_pressed_desk);
261     if (ob_a_unfocused_unpressed_desk != NULL)
262         appearance_free(ob_a_unfocused_unpressed_desk);
263     if (ob_a_unfocused_pressed_desk != NULL)
264         appearance_free(ob_a_unfocused_pressed_desk);
265     if (ob_a_focused_unpressed_shade != NULL)
266         appearance_free(ob_a_focused_unpressed_shade);
267     if (ob_a_focused_pressed_shade != NULL)
268         appearance_free(ob_a_focused_pressed_shade);
269     if (ob_a_unfocused_unpressed_shade != NULL)
270         appearance_free(ob_a_unfocused_unpressed_shade);
271     if (ob_a_unfocused_pressed_shade != NULL)
272         appearance_free(ob_a_unfocused_pressed_shade);
273     if (ob_a_focused_unpressed_iconify != NULL)
274         appearance_free(ob_a_focused_unpressed_iconify);
275     if (ob_a_focused_pressed_iconify != NULL)
276         appearance_free(ob_a_focused_pressed_iconify);
277     if (ob_a_unfocused_unpressed_iconify != NULL)
278         appearance_free(ob_a_unfocused_unpressed_iconify);
279     if (ob_a_unfocused_pressed_iconify != NULL)
280         appearance_free(ob_a_unfocused_pressed_iconify);
281     appearance_free(ob_a_focused_grip);
282     appearance_free(ob_a_unfocused_grip);
283     appearance_free(ob_a_focused_title);
284     appearance_free(ob_a_unfocused_title);
285     appearance_free(ob_a_focused_label);
286     appearance_free(ob_a_unfocused_label);
287     appearance_free(ob_a_icon);
288     appearance_free(ob_a_focused_handle);
289     appearance_free(ob_a_unfocused_handle);
290 }
291
292 static Window createWindow(Window parent, unsigned long mask,
293                            XSetWindowAttributes *attrib)
294 {
295     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
296                          render_depth, InputOutput, render_visual,
297                          mask, attrib);
298                        
299 }
300
301 Frame *frame_new()
302 {
303     XSetWindowAttributes attrib;
304     unsigned long mask;
305     ObFrame *self;
306
307     self = g_new(ObFrame, 1);
308
309     self->frame.visible = FALSE;
310
311     /* create all of the decor windows */
312     mask = CWOverrideRedirect | CWEventMask;
313     attrib.event_mask = FRAME_EVENTMASK;
314     attrib.override_redirect = TRUE;
315     self->frame.window = createWindow(ob_root, mask, &attrib);
316
317     mask = 0;
318     self->frame.plate = createWindow(self->frame.window, mask, &attrib);
319
320     mask = CWEventMask;
321     attrib.event_mask = ELEMENT_EVENTMASK;
322     self->title = createWindow(self->frame.window, mask, &attrib);
323     self->label = createWindow(self->title, mask, &attrib);
324     self->max = createWindow(self->title, mask, &attrib);
325     self->close = createWindow(self->title, mask, &attrib);
326     self->desk = createWindow(self->title, mask, &attrib);
327     self->shade = createWindow(self->title, mask, &attrib);
328     self->icon = createWindow(self->title, mask, &attrib);
329     self->iconify = createWindow(self->title, mask, &attrib);
330     self->handle = createWindow(self->frame.window, mask, &attrib);
331     mask |= CWCursor;
332     attrib.cursor = ob_cursors.ll_angle;
333     self->lgrip = createWindow(self->handle, mask, &attrib);
334     attrib.cursor = ob_cursors.lr_angle;
335     self->rgrip = createWindow(self->handle, mask, &attrib);
336
337     /* the other stuff is shown based on decor settings */
338     XMapWindow(ob_display, self->frame.plate);
339     XMapWindow(ob_display, self->lgrip);
340     XMapWindow(ob_display, self->rgrip);
341     XMapWindow(ob_display, self->label);
342
343     /* set colors/appearance/sizes for stuff that doesn't change */
344     XSetWindowBorder(ob_display, self->frame.window, ob_s_b_color->pixel);
345     XSetWindowBorder(ob_display, self->label, ob_s_b_color->pixel);
346     XSetWindowBorder(ob_display, self->rgrip, ob_s_b_color->pixel);
347     XSetWindowBorder(ob_display, self->lgrip, ob_s_b_color->pixel);
348
349     XResizeWindow(ob_display, self->max, BUTTON_SIZE, BUTTON_SIZE);
350     XResizeWindow(ob_display, self->iconify, BUTTON_SIZE, BUTTON_SIZE);
351     XResizeWindow(ob_display, self->icon, BUTTON_SIZE, BUTTON_SIZE);
352     XResizeWindow(ob_display, self->close, BUTTON_SIZE, BUTTON_SIZE);
353     XResizeWindow(ob_display, self->desk, BUTTON_SIZE, BUTTON_SIZE);
354     XResizeWindow(ob_display, self->shade, BUTTON_SIZE, BUTTON_SIZE);
355     XResizeWindow(ob_display, self->lgrip, GRIP_WIDTH, ob_s_handle_height);
356     XResizeWindow(ob_display, self->rgrip, GRIP_WIDTH, ob_s_handle_height);
357
358     /* set up the dynamic appearances */
359     self->a_unfocused_title = appearance_copy(ob_a_unfocused_title);
360     self->a_focused_title = appearance_copy(ob_a_focused_title);
361     self->a_unfocused_label = appearance_copy(ob_a_unfocused_label);
362     self->a_focused_label = appearance_copy(ob_a_focused_label);
363     self->a_unfocused_handle = appearance_copy(ob_a_unfocused_handle);
364     self->a_focused_handle = appearance_copy(ob_a_focused_handle);
365     self->a_icon = appearance_copy(ob_a_icon);
366
367     self->max_press = self->close_press = self->desk_press = 
368         self->iconify_press = self->shade_press = FALSE;
369
370     dispatch_register(Event_X_ButtonPress | Event_X_ButtonRelease,
371                       (EventHandler)mouse_event, self);
372
373     return (Frame*)self;
374 }
375
376 static void frame_free(ObFrame *self)
377 {
378     appearance_free(self->a_unfocused_title); 
379     appearance_free(self->a_focused_title);
380     appearance_free(self->a_unfocused_label);
381     appearance_free(self->a_focused_label);
382     appearance_free(self->a_unfocused_handle);
383     appearance_free(self->a_focused_handle);
384     appearance_free(self->a_icon);
385
386     XDestroyWindow(ob_display, self->frame.window);
387
388     dispatch_register(0, (EventHandler)mouse_event, self);
389
390     g_free(self);
391 }
392
393 void frame_show(ObFrame *self)
394 {
395     if (!self->frame.visible) {
396         self->frame.visible = TRUE;
397         XMapWindow(ob_display, self->frame.window);
398     }
399 }
400
401 void frame_hide(ObFrame *self)
402 {
403     if (self->frame.visible) {
404         self->frame.visible = FALSE;
405         self->frame.client->ignore_unmaps++;
406         XUnmapWindow(ob_display, self->frame.window);
407     }
408 }
409
410 void frame_adjust_shape(ObFrame *self)
411 {
412 #ifdef SHAPE
413     int num;
414     XRectangle xrect[2];
415
416     if (!self->frame.client->shaped) {
417         /* clear the shape on the frame window */
418         XShapeCombineMask(ob_display, self->frame.window, ShapeBounding,
419                           self->innersize.left,
420                           self->innersize.top,
421                           None, ShapeSet);
422     } else {
423         /* make the frame's shape match the clients */
424         XShapeCombineShape(ob_display, self->frame.window, ShapeBounding,
425                            self->innersize.left,
426                            self->innersize.top,
427                            self->frame.client->window,
428                            ShapeBounding, ShapeSet);
429
430         num = 0;
431         if (self->frame.client->decorations & Decor_Titlebar) {
432             xrect[0].x = -ob_s_bevel;
433             xrect[0].y = -ob_s_bevel;
434             xrect[0].width = self->width + self->bwidth * 2;
435             xrect[0].height = TITLE_HEIGHT +
436                 self->bwidth * 2;
437             ++num;
438         }
439
440         if (self->frame.client->decorations & Decor_Handle) {
441             xrect[1].x = -ob_s_bevel;
442             xrect[1].y = HANDLE_Y(self);
443             xrect[1].width = self->width + self->bwidth * 2;
444             xrect[1].height = ob_s_handle_height +
445                 self->bwidth * 2;
446             ++num;
447         }
448
449         XShapeCombineRectangles(ob_display, self->frame.window,
450                                 ShapeBounding, 0, 0, xrect, num,
451                                 ShapeUnion, Unsorted);
452     }
453 #endif
454 }
455
456 void frame_adjust_area(ObFrame *self, gboolean moved, gboolean resized)
457 {
458     if (resized) {
459         if (self->frame.client->decorations & Decor_Border) {
460             self->bwidth = ob_s_bwidth;
461             self->cbwidth = ob_s_cbwidth;
462         } else {
463             self->bwidth = self->cbwidth = 0;
464         }
465         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
466                   self->cbwidth, self->cbwidth);
467         self->width = self->frame.client->area.width + self->cbwidth * 2;
468         g_assert(self->width > 0);
469
470         /* set border widths */
471         XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
472         XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
473         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
474         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
475         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
476         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
477   
478         /* position/size and map/unmap all the windows */
479
480         /* they all default off, they're turned on in layout_title */
481         self->icon_x = -1;
482         self->desk_x = -1;
483         self->shade_x = -1;
484         self->iconify_x = -1;
485         self->label_x = -1;
486         self->max_x = -1;
487         self->close_x = -1;
488
489         if (self->frame.client->decorations & Decor_Titlebar) {
490             XMoveResizeWindow(ob_display, self->title,
491                               -self->bwidth, -self->bwidth,
492                               self->width, TITLE_HEIGHT);
493             self->innersize.top += TITLE_HEIGHT + self->bwidth;
494             XMapWindow(ob_display, self->title);
495
496             RECT_SET(self->a_focused_title->area, 0, 0,
497                      self->width, TITLE_HEIGHT);
498             RECT_SET(self->a_unfocused_title->area, 0, 0,
499                      self->width, TITLE_HEIGHT);
500
501             /* layout the title bar elements */
502             layout_title(self);
503         } else
504             XUnmapWindow(ob_display, self->title);
505
506         if (self->frame.client->decorations & Decor_Handle) {
507             XMoveResizeWindow(ob_display, self->handle,
508                               -self->bwidth, HANDLE_Y(self),
509                               self->width, ob_s_handle_height);
510             XMoveWindow(ob_display, self->lgrip,
511                         -self->bwidth, -self->bwidth);
512             XMoveWindow(ob_display, self->rgrip,
513                         -self->bwidth + self->width -
514                         GRIP_WIDTH, -self->bwidth);
515             self->innersize.bottom += ob_s_handle_height +
516                 self->bwidth;
517             XMapWindow(ob_display, self->handle);
518
519             if (ob_a_focused_grip->surface.data.planar.grad ==
520                 Background_ParentRelative)
521                 RECT_SET(self->a_focused_handle->area, 0, 0,
522                          self->width, ob_s_handle_height);
523             else
524                 RECT_SET(self->a_focused_handle->area,
525                          GRIP_WIDTH + self->bwidth, 0,
526                          self->width - (GRIP_WIDTH + self->bwidth) * 2,
527                          ob_s_handle_height);
528             if (ob_a_unfocused_grip->surface.data.planar.grad ==
529                 Background_ParentRelative)
530                 RECT_SET(self->a_unfocused_handle->area, 0, 0,
531                          self->width, ob_s_handle_height);
532             else
533                 RECT_SET(self->a_unfocused_handle->area,
534                          GRIP_WIDTH + self->bwidth, 0,
535                          self->width - (GRIP_WIDTH + self->bwidth) * 2,
536                          ob_s_handle_height);
537
538         } else
539             XUnmapWindow(ob_display, self->handle);
540     }
541
542     if (resized) {
543         /* move and resize the plate */
544         XMoveResizeWindow(ob_display, self->frame.plate,
545                           self->innersize.left - self->cbwidth,
546                           self->innersize.top - self->cbwidth,
547                           self->frame.client->area.width,
548                           self->frame.client->area.height);
549         /* when the client has StaticGravity, it likes to move around. */
550         XMoveWindow(ob_display, self->frame.client->window, 0, 0);
551     }
552
553     if (resized) {
554         STRUT_SET(self->frame.size,
555                   self->innersize.left + self->bwidth,
556                   self->innersize.top + self->bwidth,
557                   self->innersize.right + self->bwidth,
558                   self->innersize.bottom + self->bwidth);
559     }
560
561     /* shading can change without being moved or resized */
562     RECT_SET_SIZE(self->frame.area,
563                   self->frame.client->area.width +
564                   self->frame.size.left + self->frame.size.right,
565                   (self->frame.client->shaded ? TITLE_HEIGHT + self->bwidth*2:
566                    self->frame.client->area.height +
567                    self->frame.size.top + self->frame.size.bottom));
568
569     if (moved) {
570         /* find the new coordinates, done after setting the frame.size, for
571            frame_client_gravity. */
572         self->frame.area.x = self->frame.client->area.x;
573         self->frame.area.y = self->frame.client->area.y;
574         frame_client_gravity((Frame*)self,
575                              &self->frame.area.x, &self->frame.area.y);
576     }
577
578     /* move and resize the top level frame.
579        shading can change without being moved or resized */
580     XMoveResizeWindow(ob_display, self->frame.window,
581                       self->frame.area.x, self->frame.area.y,
582                       self->width,
583                       self->frame.area.height - self->bwidth * 2);
584
585     if (resized) {
586         obrender_frame(self);
587
588         frame_adjust_shape(self);
589     }
590 }
591
592 void frame_adjust_state(ObFrame *self)
593 {
594     obrender_frame(self);
595 }
596
597 void frame_adjust_focus(ObFrame *self)
598 {
599     obrender_frame(self);
600 }
601
602 void frame_adjust_title(ObFrame *self)
603 {
604     obrender_frame(self);
605 }
606
607 void frame_adjust_icon(ObFrame *self)
608 {
609     obrender_frame(self);
610 }
611
612 void frame_grab_client(ObFrame *self, Client *client)
613 {
614     self->frame.client = client;
615
616     /* reparent the client to the frame */
617     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
618     /*
619       When reparenting the client window, it is usually not mapped yet, since
620       this occurs from a MapRequest. However, in the case where Openbox is
621       starting up, the window is already mapped, so we'll see unmap events for
622       it. There are 2 unmap events generated that we see, one with the 'event'
623       member set the root window, and one set to the client, but both get
624       handled and need to be ignored.
625     */
626     if (ob_state == State_Starting)
627         client->ignore_unmaps += 2;
628
629     /* select the event mask on the client's parent (to receive config/map
630        req's) the ButtonPress is to catch clicks on the client border */
631     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
632
633     /* map the client so it maps when the frame does */
634     XMapWindow(ob_display, client->window);
635
636     frame_adjust_area(self, TRUE, TRUE);
637
638     /* set all the windows for the frame in the client_map */
639     g_hash_table_insert(client_map, &self->frame.window, client);
640     g_hash_table_insert(client_map, &self->frame.plate, client);
641     g_hash_table_insert(client_map, &self->title, client);
642     g_hash_table_insert(client_map, &self->label, client);
643     g_hash_table_insert(client_map, &self->max, client);
644     g_hash_table_insert(client_map, &self->close, client);
645     g_hash_table_insert(client_map, &self->desk, client);
646     g_hash_table_insert(client_map, &self->shade, client);
647     g_hash_table_insert(client_map, &self->icon, client);
648     g_hash_table_insert(client_map, &self->iconify, client);
649     g_hash_table_insert(client_map, &self->handle, client);
650     g_hash_table_insert(client_map, &self->lgrip, client);
651     g_hash_table_insert(client_map, &self->rgrip, client);
652 }
653
654 void frame_release_client(ObFrame *self, Client *client)
655 {
656     XEvent ev;
657
658     g_assert(self->frame.client == client);
659
660     /* check if the app has already reparented its window away */
661     if (XCheckTypedWindowEvent(ob_display, client->window,
662                                ReparentNotify, &ev)) {
663         XPutBackEvent(ob_display, &ev);
664         /* re-map the window since the unmanaging process unmaps it */
665         XMapWindow(ob_display, client->window);
666     } else {
667         /* according to the ICCCM - if the client doesn't reparent itself,
668            then we will reparent the window to root for them */
669         XReparentWindow(ob_display, client->window, ob_root,
670                         client->area.x,
671                         client->area.y);
672     }
673
674     /* remove all the windows for the frame from the client_map */
675     g_hash_table_remove(client_map, &self->frame.window);
676     g_hash_table_remove(client_map, &self->frame.plate);
677     g_hash_table_remove(client_map, &self->title);
678     g_hash_table_remove(client_map, &self->label);
679     g_hash_table_remove(client_map, &self->max);
680     g_hash_table_remove(client_map, &self->close);
681     g_hash_table_remove(client_map, &self->desk);
682     g_hash_table_remove(client_map, &self->shade);
683     g_hash_table_remove(client_map, &self->icon);
684     g_hash_table_remove(client_map, &self->iconify);
685     g_hash_table_remove(client_map, &self->handle);
686     g_hash_table_remove(client_map, &self->lgrip);
687     g_hash_table_remove(client_map, &self->rgrip);
688
689     frame_free(self);
690 }
691
692 static void layout_title(ObFrame *self)
693 {
694     char *lc;
695     int x;
696     gboolean n, d, i, l, m, c, s;
697     ConfigValue layout;
698
699     n = d = i = l = m = c = s = FALSE;
700
701     if (!config_get("titlebar.layout", Config_String, &layout))
702         g_assert_not_reached();
703
704     /* figure out whats being shown, and the width of the label */
705     self->label_width = self->width - (ob_s_bevel + 1) * 2;
706     for (lc = layout.string; *lc != '\0'; ++lc) {
707         switch (*lc) {
708         case 'N':
709             if (!(self->frame.client->decorations & Decor_Icon)) break;
710             if (n) { *lc = ' '; break; } /* rm duplicates */
711             n = TRUE;
712             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
713             break;
714         case 'D':
715             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
716             if (d) { *lc = ' '; break; } /* rm duplicates */
717             d = TRUE;
718             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
719             break;
720         case 'S':
721             if (!(self->frame.client->decorations & Decor_Shade)) break;
722             if (s) { *lc = ' '; break; } /* rm duplicates */
723             s = TRUE;
724             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
725             break;
726         case 'I':
727             if (!(self->frame.client->decorations & Decor_Iconify)) break;
728             if (i) { *lc = ' '; break; } /* rm duplicates */
729             i = TRUE;
730             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
731             break;
732         case 'L':
733             if (l) { *lc = ' '; break; } /* rm duplicates */
734             l = TRUE;
735             break;
736         case 'M':
737             if (!(self->frame.client->decorations & Decor_Maximize)) break;
738             if (m) { *lc = ' '; break; } /* rm duplicates */
739             m = TRUE;
740             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
741             break;
742         case 'C':
743             if (!(self->frame.client->decorations & Decor_Close)) break;
744             if (c) { *lc = ' '; break; } /* rm duplicates */
745             c = TRUE;
746             self->label_width -= BUTTON_SIZE + ob_s_bevel + 1;
747             break;
748         }
749     }
750     if (self->label_width < 1) self->label_width = 1;
751
752     XResizeWindow(ob_display, self->label, self->label_width,
753                   LABEL_HEIGHT);
754   
755     if (!n) XUnmapWindow(ob_display, self->icon);
756     if (!d) XUnmapWindow(ob_display, self->desk);
757     if (!s) XUnmapWindow(ob_display, self->shade);
758     if (!i) XUnmapWindow(ob_display, self->iconify);
759     if (!l) XUnmapWindow(ob_display, self->label);
760     if (!m) XUnmapWindow(ob_display, self->max);
761     if (!c) XUnmapWindow(ob_display, self->close);
762
763     x = ob_s_bevel + 1;
764     for (lc = layout.string; *lc != '\0'; ++lc) {
765         switch (*lc) {
766         case 'N':
767             if (!n) break;
768             self->icon_x = x;
769             RECT_SET(self->a_icon->area, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
770             XMapWindow(ob_display, self->icon);
771             XMoveWindow(ob_display, self->icon, x, ob_s_bevel + 1);
772             x += BUTTON_SIZE + ob_s_bevel + 1;
773             break;
774         case 'D':
775             if (!d) break;
776             self->desk_x = x;
777             XMapWindow(ob_display, self->desk);
778             XMoveWindow(ob_display, self->desk, x, ob_s_bevel + 1);
779             x += BUTTON_SIZE + ob_s_bevel + 1;
780             break;
781         case 'S':
782             if (!s) break;
783             self->shade_x = x;
784             XMapWindow(ob_display, self->shade);
785             XMoveWindow(ob_display, self->shade, x, ob_s_bevel + 1);
786             x += BUTTON_SIZE + ob_s_bevel + 1;
787             break;
788         case 'I':
789             if (!i) break;
790             self->iconify_x = x;
791             XMapWindow(ob_display, self->iconify);
792             XMoveWindow(ob_display, self->iconify, x, ob_s_bevel + 1);
793             x += BUTTON_SIZE + ob_s_bevel + 1;
794             break;
795         case 'L':
796             if (!l) break;
797             self->label_x = x;
798             XMapWindow(ob_display, self->label);
799             XMoveWindow(ob_display, self->label, x, ob_s_bevel);
800             x += self->label_width + ob_s_bevel + 1;
801             break;
802         case 'M':
803             if (!m) break;
804             self->max_x = x;
805             XMapWindow(ob_display, self->max);
806             XMoveWindow(ob_display, self->max, x, ob_s_bevel + 1);
807             x += BUTTON_SIZE + ob_s_bevel + 1;
808             break;
809         case 'C':
810             if (!c) break;
811             self->close_x = x;
812             XMapWindow(ob_display, self->close);
813             XMoveWindow(ob_display, self->close, x, ob_s_bevel + 1);
814             x += BUTTON_SIZE + ob_s_bevel + 1;
815             break;
816         }
817     }
818
819     RECT_SET(self->a_focused_label->area, 0, 0,
820              self->label_width, LABEL_HEIGHT);
821     RECT_SET(self->a_unfocused_label->area, 0, 0,
822              self->label_width, LABEL_HEIGHT);
823 }
824
825 static void mouse_event(const ObEvent *e, ObFrame *self)
826 {
827     Window win;
828     gboolean press = e->type == Event_X_ButtonPress;
829
830     win = e->data.x.e->xbutton.window;
831     if (win == self->max) {
832         self->max_press = press;
833         obrender_frame(self);
834     } else if (win == self->close) {
835         self->close_press = press;
836         obrender_frame(self);
837     } else if (win == self->iconify) {
838         self->iconify_press = press;
839         obrender_frame(self);
840     } else if (win == self->desk) { 
841         self->desk_press = press;
842         obrender_frame(self);
843     } else if (win == self->shade) { 
844         self->shade_press = press;
845         obrender_frame(self);
846     }
847 }
848
849 Context get_context(Client *client, Window win)
850 {
851     ObFrame *self;
852
853     if (win == ob_root) return Context_Root;
854     if (client == NULL) return Context_None;
855     if (win == client->window) return Context_Client;
856
857     self = (ObFrame*) client->frame;
858     if (win == self->frame.window) return Context_Frame;
859     if (win == self->frame.plate)  return Context_Client;
860     if (win == self->title)  return Context_Titlebar;
861     if (win == self->label)  return Context_Titlebar;
862     if (win == self->handle) return Context_Handle;
863     if (win == self->lgrip)  return Context_BLCorner;
864     if (win == self->rgrip)  return Context_BRCorner;
865     if (win == self->max)  return Context_Maximize;
866     if (win == self->iconify)  return Context_Iconify;
867     if (win == self->close)  return Context_Close;
868     if (win == self->icon)  return Context_Icon;
869     if (win == self->desk)  return Context_AllDesktops;
870     if (win == self->shade)  return Context_Shade;
871
872     return Context_None;
873 }