merge the C branch into HEAD
[mikachu/openbox.git] / c / frame.c
1 #include "openbox.h"
2 #include "frame.h"
3 #include "extensions.h"
4 #include "hooks.h"
5
6 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
7 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask)
8
9 static Window createWindow(Window parent, unsigned long mask,
10                            XSetWindowAttributes *attrib)
11 {
12     /* XXX DONT USE THE DEFAULT SHIT */
13     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
14                          DefaultDepth(ob_display, ob_screen), InputOutput,
15                          DefaultVisual(ob_display, ob_screen),
16                          mask, attrib);
17                        
18 }
19
20 Frame *frame_new(Client *client)
21 {
22     XSetWindowAttributes attrib;
23     unsigned long mask;
24     Frame *self;
25
26     self = g_new(Frame, 1);
27
28     self->client = client;
29     self->visible = FALSE;
30
31     /* create all of the decor windows */
32     mask = CWOverrideRedirect | CWEventMask;
33     attrib.event_mask = FRAME_EVENTMASK;
34     attrib.override_redirect = TRUE;
35     self->window = createWindow(ob_root, mask, &attrib);
36
37     mask = 0;
38     self->plate = createWindow(self->window, mask, &attrib);
39     mask = CWEventMask;
40     attrib.event_mask = (ButtonPressMask | ButtonReleaseMask |
41                          ButtonMotionMask | ExposureMask);
42     self->title = createWindow(self->window, mask, &attrib);
43     self->label = createWindow(self->title, mask, &attrib);
44     self->max = createWindow(self->title, mask, &attrib);
45     self->close = createWindow(self->title, mask, &attrib);
46     self->desk = createWindow(self->title, mask, &attrib);
47     self->icon = createWindow(self->title, mask, &attrib);
48     self->iconify = createWindow(self->title, mask, &attrib);
49     self->handle = createWindow(self->window, mask, &attrib);
50     mask |= CWCursor;
51     attrib.cursor = ob_cursors.ll_angle;
52     self->lgrip = createWindow(self->handle, mask, &attrib);
53     attrib.cursor = ob_cursors.lr_angle;
54     self->rgrip = createWindow(self->handle, mask, &attrib);
55
56     /* the other stuff is shown based on decor settings */
57     XMapWindow(ob_display, self->plate);
58     XMapWindow(ob_display, self->lgrip);
59     XMapWindow(ob_display, self->rgrip);
60     XMapWindow(ob_display, self->label);
61
62
63     /* XXX TEMPORARY OF COURSE!@&*(@! */
64
65     XSetWindowBackground(ob_display, self->title, 0x3333aa);
66     XSetWindowBackground(ob_display, self->handle, 0x3333aa);
67     XSetWindowBackground(ob_display, self->lgrip, 0x2233aa);
68     XSetWindowBackground(ob_display, self->rgrip, 0x2233aa);
69
70     XSetWindowBorder(ob_display, self->window, 0);
71     XSetWindowBorder(ob_display, self->label, 0);
72     XSetWindowBorder(ob_display, self->rgrip, 0);
73     XSetWindowBorder(ob_display, self->lgrip, 0);
74     XSetWindowBorder(ob_display, self->plate, 0x771122);
75
76     /* XXX /TEMPORARY OF COURSE!@&*(@! */
77
78     /* set all the windows for the frame in the client_map */
79     g_hash_table_insert(client_map, (gpointer)self->window, self->client);
80     g_hash_table_insert(client_map, (gpointer)self->plate, self->client);
81     g_hash_table_insert(client_map, (gpointer)self->title, self->client);
82     g_hash_table_insert(client_map, (gpointer)self->label, self->client);
83     g_hash_table_insert(client_map, (gpointer)self->max, self->client);
84     g_hash_table_insert(client_map, (gpointer)self->close, self->client);
85     g_hash_table_insert(client_map, (gpointer)self->desk, self->client);
86     g_hash_table_insert(client_map, (gpointer)self->icon, self->client);
87     g_hash_table_insert(client_map, (gpointer)self->iconify, self->client);
88     g_hash_table_insert(client_map, (gpointer)self->handle, self->client);
89     g_hash_table_insert(client_map, (gpointer)self->lgrip, self->client);
90     g_hash_table_insert(client_map, (gpointer)self->rgrip, self->client);
91
92     return self;
93 }
94
95 void frame_free(Frame *self)
96 {
97     /* remove all the windows for the frame from the client_map */
98     g_hash_table_remove(client_map, (gpointer)self->window);
99     g_hash_table_remove(client_map, (gpointer)self->plate);
100     g_hash_table_remove(client_map, (gpointer)self->title);
101     g_hash_table_remove(client_map, (gpointer)self->label);
102     g_hash_table_remove(client_map, (gpointer)self->max);
103     g_hash_table_remove(client_map, (gpointer)self->close);
104     g_hash_table_remove(client_map, (gpointer)self->desk);
105     g_hash_table_remove(client_map, (gpointer)self->icon);
106     g_hash_table_remove(client_map, (gpointer)self->iconify);
107     g_hash_table_remove(client_map, (gpointer)self->handle);
108     g_hash_table_remove(client_map, (gpointer)self->lgrip);
109     g_hash_table_remove(client_map, (gpointer)self->rgrip);
110
111     XDestroyWindow(ob_display, self->window);
112
113     g_free(self);
114 }
115
116 void frame_grab_client(Frame *self)
117 {
118     /* reparent the client to the frame */
119     XReparentWindow(ob_display, self->client->window, self->plate, 0, 0);
120     /*
121       When reparenting the client window, it is usually not mapped yet, since
122       this occurs from a MapRequest. However, in the case where Openbox is
123       starting up, the window is already mapped, so we'll see unmap events for
124       it. There are 2 unmap events generated that we see, one with the 'event'
125       member set the root window, and one set to the client, but both get
126       handled and need to be ignored.
127     */
128     if (ob_state == State_Starting)
129         self->client->ignore_unmaps += 2;
130
131     /* select the event mask on the client's parent (to receive config/map
132        req's) the ButtonPress is to catch clicks on the client border */
133     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
134
135     /* map the client so it maps when the frame does */
136     XMapWindow(ob_display, self->client->window);
137
138     frame_adjust_size(self);
139     frame_adjust_position(self);
140 }
141
142 void frame_release_client(Frame *self)
143 {
144     XEvent ev;
145
146     /* check if the app has already reparented its window away */
147     if (XCheckTypedWindowEvent(ob_display, self->client->window,
148                                ReparentNotify, &ev)) {
149         XPutBackEvent(ob_display, &ev);
150         /* re-map the window since the unmanaging process unmaps it */
151         XMapWindow(ob_display, self->client->window);
152     } else {
153         /* according to the ICCCM - if the client doesn't reparent itself,
154            then we will reparent the window to root for them */
155         XReparentWindow(ob_display, self->client->window, ob_root,
156                         self->client->area.x, self->client->area.y);
157     }
158 }
159
160 void frame_show(Frame *self)
161 {
162     if (!self->visible) {
163         self->visible = TRUE;
164         XMapWindow(ob_display, self->window);
165         LOGICALHOOK(WindowShow, g_quark_try_string("client"), self->client);
166     }
167 }
168
169 void frame_hide(Frame *self)
170 {
171     if (self->visible) {
172         self->visible = FALSE;
173         self->client->ignore_unmaps++;
174         XUnmapWindow(ob_display, self->window);
175         LOGICALHOOK(WindowHide, g_quark_try_string("client"), self->client);
176     }
177 }
178
179 void frame_adjust_size(Frame *self)
180 {
181     self->decorations = self->client->decorations;
182
183     /* XXX set shit from the style */
184     self->geom.font_height = 10;
185     self->geom.bevel = 1;
186     self->geom.button_size = self->geom.font_height - 2;
187     self->geom.handle_height = 2;
188     self->geom.grip_width = self->geom.button_size * 2;
189     XResizeWindow(ob_display, self->lgrip, self->geom.grip_width,
190                   self->geom.handle_height);
191     XResizeWindow(ob_display, self->rgrip, self->geom.grip_width,
192                   self->geom.handle_height);
193           
194      
195      
196           
197     if (self->decorations & Decor_Border) {
198         self->geom.bwidth = 1;/*XXX style->frameBorderWidth(); */
199         self->geom.cbwidth = 1; /*XXX style->clientBorderWidth(); */
200     } else {
201         self->geom.bwidth = self->geom.cbwidth = 0;
202     }
203     STRUT_SET(self->innersize, self->geom.cbwidth, self->geom.cbwidth,
204               self->geom.cbwidth, self->geom.cbwidth);
205     self->geom.width = self->client->area.width + self->geom.cbwidth * 2;
206     g_assert(self->geom.width > 0);
207
208     /* set border widths */
209     XSetWindowBorderWidth(ob_display, self->plate,  self->geom.cbwidth);
210     XSetWindowBorderWidth(ob_display, self->window, self->geom.bwidth);
211     XSetWindowBorderWidth(ob_display, self->title,  self->geom.bwidth);
212     XSetWindowBorderWidth(ob_display, self->handle, self->geom.bwidth);
213     XSetWindowBorderWidth(ob_display, self->lgrip,  self->geom.bwidth);
214     XSetWindowBorderWidth(ob_display, self->rgrip,  self->geom.bwidth);
215   
216     /* position/size and map/unmap all the windows */
217
218     if (self->decorations & Decor_Titlebar) {
219         self->geom.title_height = self->geom.font_height +
220             self->geom.bevel * 2;
221         XMoveResizeWindow(ob_display, self->title,
222                           -self->geom.bwidth, -self->geom.bwidth,
223                           self->geom.width, self->geom.title_height);
224         self->innersize.top += self->geom.title_height + self->geom.bwidth;
225         XMapWindow(ob_display, self->title);
226
227         /* layout the title bar elements */
228         /*XXX layoutTitle(); */
229     } else {
230         XUnmapWindow(ob_display, self->title);
231         /* make all the titlebar stuff not render */
232         self->decorations &= ~(Decor_Icon | Decor_Iconify |
233                                Decor_Maximize | Decor_Close |
234                                Decor_AllDesktops);
235     }
236
237     if (self->decorations & Decor_Handle) {
238         self->geom.handle_y = self->innersize.top +
239             self->client->area.height + self->geom.cbwidth;
240         XMoveResizeWindow(ob_display, self->handle,
241                           -self->geom.bwidth, self->geom.handle_y,
242                           self->geom.width, self->geom.handle_height);
243         XMoveWindow(ob_display, self->lgrip,
244                     -self->geom.bwidth, -self->geom.bwidth);
245         XMoveWindow(ob_display, self->rgrip,
246                     -self->geom.bwidth + self->geom.width -
247                     self->geom.grip_width, -self->geom.bwidth);
248         self->innersize.bottom += self->geom.handle_height +
249             self->geom.bwidth;
250         XMapWindow(ob_display, self->handle);
251     } else
252         XUnmapWindow(ob_display, self->handle);
253   
254     XResizeWindow(ob_display, self->window, self->geom.width,
255                   (self->client->shaded ? self->geom.title_height :
256                    self->innersize.top + self->innersize.bottom +
257                    self->client->area.height));
258
259     /* do this in two steps because clients whose gravity is set to
260        'Static' don't end up getting moved at all with an XMoveResizeWindow */
261     XMoveWindow(ob_display, self->plate,
262                 self->innersize.left - self->geom.cbwidth,
263                 self->innersize.top - self->geom.cbwidth);
264     XResizeWindow(ob_display, self->plate, self->client->area.width,
265                   self->client->area.height);
266
267     STRUT_SET(self->size,
268               self->innersize.left + self->geom.bwidth,
269               self->innersize.right + self->geom.bwidth,
270               self->innersize.top + self->geom.bwidth,
271               self->innersize.bottom + self->geom.bwidth);
272
273     RECT_SET_SIZE(self->area,
274                   self->client->area.width +
275                   self->size.left + self->size.right,
276                   self->client->area.height +
277                   self->size.top + self->size.bottom);
278
279     /*
280     // render all the elements
281     int screen = _client->screen();
282     bool focus = _client->focused();
283     if (_decorations & Client::Decor_Titlebar) {
284     render(screen, otk::Size(geom.width, geom.title_height()), _title,
285     &_title_sur, *(focus ? style->titlebarFocusBackground() :
286     style->titlebarUnfocusBackground()), false);
287     
288     renderLabel();
289     renderMax();
290     renderDesk();
291     renderIconify();
292     renderIcon();
293     renderClose();
294     }
295
296     if (_decorations & Client::Decor_Handle) {
297     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
298     &_handle_sur, *(focus ? style->handleFocusBackground() :
299     style->handleUnfocusBackground()));
300     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
301     &_grip_sur, *(focus ? style->gripFocusBackground() :
302     style->gripUnfocusBackground()));
303     if ((focus ? style->gripFocusBackground() :
304     style->gripUnfocusBackground())->parentRelative())
305     XSetWindowBackgroundPixmap(**otk::display, _rgrip, ParentRelative);
306     else {
307     XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
308     }
309     XClearWindow(**otk::display, _rgrip);
310     }
311
312     XSetWindowBorder(**otk::display, _plate,
313     focus ? style->clientBorderFocusColor()->pixel() :
314     style->clientBorderUnfocusColor()->pixel());
315
316     */
317      
318     frame_adjust_shape(self);
319 }
320
321 void frame_adjust_position(Frame *self)
322 {
323     self->area.x = self->client->area.x;
324     self->area.y = self->client->area.y;
325     frame_client_gravity(self, &self->area.x, &self->area.y);
326     XMoveWindow(ob_display, self->window, self->area.x, self->area.y);
327 }
328
329 void frame_adjust_shape(Frame *self)
330 {
331 #ifdef SHAPE
332     int num;
333     XRectangle xrect[2];
334
335     if (!self->client->shaped) {
336         /* clear the shape on the frame window */
337         XShapeCombineMask(ob_display, self->window, ShapeBounding,
338                           self->innersize.left,
339                           self->innersize.top,
340                           None, ShapeSet);
341     } else {
342         /* make the frame's shape match the clients */
343         XShapeCombineShape(ob_display, self->window, ShapeBounding,
344                            self->innersize.left,
345                            self->innersize.top,
346                            self->client->window, ShapeBounding, ShapeSet);
347
348         num = 0;
349         if (self->decorations & Decor_Titlebar) {
350             xrect[0].x = -self->geom.bevel;
351             xrect[0].y = -self->geom.bevel;
352             xrect[0].width = self->geom.width + self->geom.bwidth * 2;
353             xrect[0].height = self->geom.title_height +
354                 self->geom.bwidth * 2;
355             ++num;
356         }
357
358         if (self->decorations & Decor_Handle) {
359             xrect[1].x = -self->geom.bevel;
360             xrect[1].y = self->geom.handle_y;
361             xrect[1].width = self->geom.width + self->geom.bwidth * 2;
362             xrect[1].height = self->geom.handle_height +
363                 self->geom.bwidth * 2;
364             ++num;
365         }
366
367         XShapeCombineRectangles(ob_display, self->window,
368                                 ShapeBounding, 0, 0, xrect, num,
369                                 ShapeUnion, Unsorted);
370     }
371 #endif
372 }
373
374 void frame_client_gravity(Frame *self, int *x, int *y)
375 {
376     /* horizontal */
377     switch (self->client->gravity) {
378     default:
379     case NorthWestGravity:
380     case SouthWestGravity:
381     case WestGravity:
382         break;
383
384     case NorthGravity:
385     case SouthGravity:
386     case CenterGravity:
387         *x -= (self->size.left + self->size.right) / 2;
388         break;
389
390     case NorthEastGravity:
391     case SouthEastGravity:
392     case EastGravity:
393         *x -= self->size.left + self->size.right;
394         break;
395
396     case ForgetGravity:
397     case StaticGravity:
398         *x -= self->size.left;
399         break;
400     }
401
402     /* vertical */
403     switch (self->client->gravity) {
404     default:
405     case NorthWestGravity:
406     case NorthEastGravity:
407     case NorthGravity:
408         break;
409
410     case CenterGravity:
411     case EastGravity:
412     case WestGravity:
413         *y -= (self->size.top + self->size.bottom) / 2;
414         break;
415
416     case SouthWestGravity:
417     case SouthEastGravity:
418     case SouthGravity:
419         *y -= self->size.top + self->size.bottom;
420         break;
421
422     case ForgetGravity:
423     case StaticGravity:
424         *y -= self->size.top;
425         break;
426     }
427 }
428
429 void frame_frame_gravity(Frame *self, int *x, int *y)
430 {
431     /* horizontal */
432     switch (self->client->gravity) {
433     default:
434     case NorthWestGravity:
435     case WestGravity:
436     case SouthWestGravity:
437         break;
438     case NorthGravity:
439     case CenterGravity:
440     case SouthGravity:
441         *x += (self->size.left + self->size.right) / 2;
442         break;
443     case NorthEastGravity:
444     case EastGravity:
445     case SouthEastGravity:
446         *x += self->size.left + self->size.right;
447         break;
448     case StaticGravity:
449     case ForgetGravity:
450         x += self->size.left;
451         break;
452     }
453
454     /* vertical */
455     switch (self->client->gravity) {
456     default:
457     case NorthWestGravity:
458     case WestGravity:
459     case SouthWestGravity:
460         break;
461     case NorthGravity:
462     case CenterGravity:
463     case SouthGravity:
464         *y += (self->size.top + self->size.bottom) / 2;
465         break;
466     case NorthEastGravity:
467     case EastGravity:
468     case SouthEastGravity:
469         *y += self->size.top + self->size.bottom;
470         break;
471     case StaticGravity:
472     case ForgetGravity:
473         *y += self->size.top;
474         break;
475     }
476 }
477
478 void frame_adjust_state(Frame *self)
479 {
480     /* XXX do shit.. buttons? */
481 }
482
483 void frame_adjust_focus(Frame *self)
484 {
485     /* XXX optimizations later... */
486     frame_adjust_size(self);
487 }
488
489 void frame_adjust_title(Frame *self)
490 {
491     /* XXX optimizations later... */
492     frame_adjust_size(self);
493 }
494
495 void frame_adjust_icon(Frame *self)
496 {
497     /* XXX render icon */
498 }
499
500 GQuark frame_get_context(Client *client, Window win)
501 {
502     Frame *self;
503
504     if (win == ob_root) return g_quark_try_string("root");
505     if (client == NULL) return g_quark_try_string("none");
506     if (win == client->window) return g_quark_try_string("client");
507
508     self = client->frame;
509     if (win == self->window) return g_quark_try_string("frame");
510     if (win == self->plate)  return g_quark_try_string("frame");
511     if (win == self->title)  return g_quark_try_string("titlebar");
512     if (win == self->label)  return g_quark_try_string("titlebar");
513     if (win == self->handle) return g_quark_try_string("handle");
514     if (win == self->lgrip)  return g_quark_try_string("blcorner");
515     if (win == self->rgrip)  return g_quark_try_string("brcorner");
516
517     return g_quark_try_string("none");
518 }
519
520 void frame_startup(void)
521 {
522     g_quark_from_string("none");
523     g_quark_from_string("root");
524     g_quark_from_string("client");
525     g_quark_from_string("titlebar");
526     g_quark_from_string("handle");
527     g_quark_from_string("frame");
528     g_quark_from_string("blcorner");
529     g_quark_from_string("brcorner");
530     g_quark_from_string("tlcorner");
531     g_quark_from_string("trcorner");
532     g_quark_from_string("foo");
533 }