]> icculus.org git repositories - dana/openbox.git/blob - openbox/frame.c
add ability to set a texture to None
[dana/openbox.git] / openbox / frame.c
1 #include "frame.h"
2 #include "openbox.h"
3 #include "extensions.h"
4 #include "framerender.h"
5
6 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
7 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
8                          ButtonPressMask | ButtonReleaseMask)
9 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
10                            ButtonMotionMask | ExposureMask)
11
12 /* XXX temp */
13 static int theme_bwidth = 3;
14 static int theme_cbwidth = 3;
15 static int theme_title_height = 8;
16
17 void frame_startup()
18 {
19 }
20
21 void frame_shutdown()
22 {
23 }
24
25 static Window createWindow(Window parent, unsigned long mask,
26                            XSetWindowAttributes *attrib)
27 {
28     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
29                          RrInstanceDepth(ob_render_inst),
30                          InputOutput, RrInstanceVisual(ob_render_inst),
31                          mask, attrib);
32                        
33 }
34
35 Frame *frame_new()
36 {
37     XSetWindowAttributes attrib;
38     unsigned long mask;
39     Frame *self;
40
41     self = g_new(Frame, 1);
42
43     self->visible = FALSE;
44
45     /* create all of the decor windows */
46     mask = CWOverrideRedirect | CWEventMask;
47     attrib.event_mask = FRAME_EVENTMASK;
48     attrib.override_redirect = TRUE;
49     self->window = createWindow(ob_root, mask, &attrib);
50 XSetWindowBorderWidth(ob_display, self->window, 1);
51
52     mask = 0;
53     self->plate = createWindow(self->window, mask, &attrib);
54     XMapWindow(ob_display, self->plate);
55
56     mask = CWEventMask;
57     attrib.event_mask = ELEMENT_EVENTMASK;
58
59     self->framedecors = 2;
60     self->framedecor = g_new(FrameDecor, self->framedecors);
61     self->framedecor[0].obwin.type = Window_Decoration;
62     self->framedecor[0].window = createWindow(self->window, mask, &attrib);
63     self->framedecor[0].anchor = Decor_Top;
64     RECT_SET(self->framedecor[0].area, 0, 0, 150, 5);
65     self->framedecor[0].type = Decor_Titlebar;
66     self->framedecor[0].context = Context_Titlebar;
67     self->framedecor[0].sizetypex = Decor_Absolute;
68     self->framedecor[0].sizetypey = Decor_Relative;
69     self->framedecor[0].frame = self;
70 XSetWindowBorderWidth(ob_display, self->framedecor[0].window, 1);
71     XMapWindow(ob_display, self->framedecor[0].window);
72
73     self->framedecor[1].obwin.type = Window_Decoration;
74     self->framedecor[1].window = createWindow(self->window, mask, &attrib);
75     self->framedecor[1].anchor = Decor_Right;
76     RECT_SET(self->framedecor[1].area, 0, 0, 10, 100);
77     self->framedecor[1].type = Decor_Titlebar;
78     self->framedecor[1].context = Context_Titlebar;
79     self->framedecor[1].sizetypex = Decor_Absolute;
80     self->framedecor[1].sizetypey = Decor_Relative;
81     self->framedecor[1].frame = self;
82 XSetWindowBorderWidth(ob_display, self->framedecor[1].window, 1);
83     XMapWindow(ob_display, self->framedecor[1].window);
84
85     self->focused = FALSE;
86
87     self->max_press = self->close_press = self->desk_press = 
88         self->iconify_press = self->shade_press = FALSE;
89     return (Frame*)self;
90 }
91
92 static void frame_free(Frame *self)
93 {
94 /* XXX WRITEME */
95     XDestroyWindow(ob_display, self->window);
96
97     g_free(self);
98 }
99
100 void frame_show(Frame *self)
101 {
102     if (!self->visible) {
103         self->visible = TRUE;
104         XMapWindow(ob_display, self->window);
105         XSync(ob_display, FALSE);
106     }
107 }
108
109 void frame_hide(Frame *self)
110 {
111     if (self->visible) {
112         self->visible = FALSE;
113         self->client->ignore_unmaps++;
114         XUnmapWindow(ob_display, self->window);
115     }
116 }
117
118 void frame_adjust_shape(Frame *self)
119 {
120 #ifdef SHAPEAGAERGGREA
121     int num;
122     XRectangle xrect[2];
123
124     if (!self->client->shaped) {
125         /* clear the shape on the frame window */
126         XShapeCombineMask(ob_display, self->window, ShapeBounding,
127                           self->innersize.left,
128                           self->innersize.top,
129                           None, ShapeSet);
130     } else {
131         /* make the frame's shape match the clients */
132         XShapeCombineShape(ob_display, self->window, ShapeBounding,
133                            self->innersize.left,
134                            self->innersize.top,
135                            self->client->window,
136                            ShapeBounding, ShapeSet);
137
138         num = 0;
139         if (self->client->decorations & Decor_Titlebar) {
140             xrect[0].x = -theme_bevel;
141             xrect[0].y = -theme_bevel;
142             xrect[0].width = self->width + self->bwidth * 2;
143             xrect[0].height = theme_title_height +
144                 self->bwidth * 2;
145             ++num;
146         }
147
148         if (self->client->decorations & Decor_Handle) {
149             xrect[1].x = -theme_bevel;
150             xrect[1].y = FRAME_HANDLE_Y(self);
151             xrect[1].width = self->width + self->bwidth * 2;
152             xrect[1].height = theme_handle_height +
153                 self->bwidth * 2;
154             ++num;
155         }
156
157         XShapeCombineRectangles(ob_display, self->window,
158                                 ShapeBounding, 0, 0, xrect, num,
159                                 ShapeUnion, Unsorted);
160     }
161 #endif
162 }
163
164 void frame_adjust_state(Frame *self)
165 {
166     framerender_frame(self);
167 }
168
169 void frame_adjust_focus(Frame *self, gboolean hilite)
170 {
171     self->focused = hilite;
172     framerender_frame(self);
173 }
174
175 void frame_adjust_title(Frame *self)
176 {
177     framerender_frame(self);
178 }
179
180 void frame_adjust_icon(Frame *self)
181 {
182     framerender_frame(self);
183 }
184
185 void frame_grab_client(Frame *self, Client *client)
186 {
187     int i;
188     self->client = client;
189
190     /* reparent the client to the frame */
191     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
192     /*
193       When reparenting the client window, it is usually not mapped yet, since
194       this occurs from a MapRequest. However, in the case where Openbox is
195       starting up, the window is already mapped, so we'll see unmap events for
196       it. There are 2 unmap events generated that we see, one with the 'event'
197       member set the root window, and one set to the client, but both get
198       handled and need to be ignored.
199     */
200     if (ob_state == State_Starting)
201         client->ignore_unmaps += 2;
202
203     /* select the event mask on the client's parent (to receive config/map
204        req's) the ButtonPress is to catch clicks on the client border */
205     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
206
207     /* map the client so it maps when the frame does */
208     XMapWindow(ob_display, client->window);
209
210     frame_adjust_area(self, TRUE, TRUE);
211
212     /* set all the windows for the frame in the window_map */
213     g_hash_table_insert(window_map, &self->window, client);
214     g_hash_table_insert(window_map, &self->plate, client);
215
216     for (i = 0; i < self->framedecors; i++)
217         g_hash_table_insert(window_map, &self->framedecor[i].window, 
218                             &self->framedecor[i]);
219 }
220
221 void frame_release_client(Frame *self, Client *client)
222 {
223     int i;
224     XEvent ev;
225
226     g_assert(self->client == client);
227
228     /* check if the app has already reparented its window away */
229     if (XCheckTypedWindowEvent(ob_display, client->window,
230                                ReparentNotify, &ev)) {
231         XPutBackEvent(ob_display, &ev);
232
233         /* re-map the window since the unmanaging process unmaps it */
234
235         /* XXX ... um no it doesnt it unmaps its parent, the window itself
236            retains its mapped state, no?! XXX
237            XMapWindow(ob_display, client->window); */
238     } else {
239         /* according to the ICCCM - if the client doesn't reparent itself,
240            then we will reparent the window to root for them */
241         XReparentWindow(ob_display, client->window, ob_root,
242                         client->area.x,
243                         client->area.y);
244     }
245
246     /* remove all the windows for the frame from the window_map */
247     g_hash_table_remove(window_map, &self->window);
248     g_hash_table_remove(window_map, &self->plate);
249
250     for (i = 0; i < self->framedecors; i++)
251         g_hash_table_remove(window_map, &self->framedecor[i].window);
252
253     frame_free(self);
254 }
255
256 Context frame_context_from_string(char *name)
257 {
258     if (!g_ascii_strcasecmp("root", name))
259         return Context_Root;
260     else if (!g_ascii_strcasecmp("client", name))
261         return Context_Client;
262     else if (!g_ascii_strcasecmp("titlebar", name))
263         return Context_Titlebar;
264     else if (!g_ascii_strcasecmp("handle", name))
265         return Context_Handle;
266     else if (!g_ascii_strcasecmp("frame", name))
267         return Context_Frame;
268     else if (!g_ascii_strcasecmp("blcorner", name))
269         return Context_BLCorner;
270     else if (!g_ascii_strcasecmp("tlcorner", name))
271         return Context_TLCorner;
272     else if (!g_ascii_strcasecmp("brcorner", name))
273         return Context_BRCorner;
274     else if (!g_ascii_strcasecmp("trcorner", name))
275         return Context_TRCorner;
276     else if (!g_ascii_strcasecmp("maximize", name))
277         return Context_Maximize;
278     else if (!g_ascii_strcasecmp("alldesktops", name))
279         return Context_AllDesktops;
280     else if (!g_ascii_strcasecmp("shade", name))
281         return Context_Shade;
282     else if (!g_ascii_strcasecmp("iconify", name))
283         return Context_Iconify;
284     else if (!g_ascii_strcasecmp("icon", name))
285         return Context_Icon;
286     else if (!g_ascii_strcasecmp("close", name))
287         return Context_Close;
288     return Context_None;
289 }
290
291 Context frame_context(Client *client, Window win)
292 {
293     ObWindow *obwin;
294     if (win == ob_root) return Context_Root;
295     if (client == NULL) return Context_None;
296     if (win == client->window) return Context_Client;
297
298     obwin = g_hash_table_lookup(window_map, &win);
299     g_assert(obwin);
300
301     if (client->frame->window == win) {
302 g_print("frame context\n");
303         return Context_Frame;
304     }
305     if (client->frame->plate == win)
306         return Context_Client;
307 g_print("decoration clicked\n");
308     g_assert(WINDOW_IS_DECORATION(obwin));
309     return WINDOW_AS_DECORATION(obwin)->context;
310 }
311
312 void frame_client_gravity(Frame *self, int *x, int *y)
313 {
314     /* horizontal */
315     switch (self->client->gravity) {
316     default:
317     case NorthWestGravity:
318     case SouthWestGravity:
319     case WestGravity:
320         break;
321
322     case NorthGravity:
323     case SouthGravity:
324     case CenterGravity:
325         *x -= (self->size.left + self->size.right) / 2;
326         break;
327
328     case NorthEastGravity:
329     case SouthEastGravity:
330     case EastGravity:
331         *x -= self->size.left + self->size.right;
332         break;
333
334     case ForgetGravity:
335     case StaticGravity:
336         *x -= self->size.left;
337         break;
338     }
339
340     /* vertical */
341     switch (self->client->gravity) {
342     default:
343     case NorthWestGravity:
344     case NorthEastGravity:
345     case NorthGravity:
346         break;
347
348     case CenterGravity:
349     case EastGravity:
350     case WestGravity:
351         *y -= (self->size.top + self->size.bottom) / 2;
352         break;
353
354     case SouthWestGravity:
355     case SouthEastGravity:
356     case SouthGravity:
357         *y -= self->size.top + self->size.bottom;
358         break;
359
360     case ForgetGravity:
361     case StaticGravity:
362         *y -= self->size.top;
363         break;
364     }
365 }
366
367 void frame_frame_gravity(Frame *self, int *x, int *y)
368 {
369     /* horizontal */
370     switch (self->client->gravity) {
371     default:
372     case NorthWestGravity:
373     case WestGravity:
374     case SouthWestGravity:
375         break;
376     case NorthGravity:
377     case CenterGravity:
378     case SouthGravity:
379         *x += (self->size.left + self->size.right) / 2;
380         break;
381     case NorthEastGravity:
382     case EastGravity:
383     case SouthEastGravity:
384         *x += self->size.left + self->size.right;
385         break;
386     case StaticGravity:
387     case ForgetGravity:
388         *x += self->size.left;
389         break;
390     }
391
392     /* vertical */
393     switch (self->client->gravity) {
394     default:
395     case NorthWestGravity:
396     case WestGravity:
397     case SouthWestGravity:
398         break;
399     case NorthGravity:
400     case CenterGravity:
401     case SouthGravity:
402         *y += (self->size.top + self->size.bottom) / 2;
403         break;
404     case NorthEastGravity:
405     case EastGravity:
406     case SouthEastGravity:
407         *y += self->size.top + self->size.bottom;
408         break;
409     case StaticGravity:
410     case ForgetGravity:
411         *y += self->size.top;
412         break;
413     }
414 }
415
416 void decor_calculate_size(FrameDecor *d, Rect *r)
417 {
418     r->x = d->area.x;
419     r->y = d->area.y;
420
421     switch (d->sizetypex) {
422     case Decor_Absolute:
423        r->width = d->area.width;
424     break;
425     case Decor_Relative:
426        r->width = d->frame->client->area.width * d->area.width / 100;
427     break;
428     }
429
430     switch (d->sizetypey) {
431     case Decor_Absolute:
432        r->height = d->area.height;
433     break;
434     case Decor_Relative:
435        r->height = d->frame->client->area.height * d->area.height / 100;
436     break;
437     }
438 g_print("area of decoration is %d, %d, %d, %d\n", r->x, r->y, r->width, 
439 r->height);
440 }
441
442 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
443 {
444     FrameDecor *dec;
445     Rect *cr, area;
446     int i, le = 0, re = 0, te = 0, be = 0, temp, x, y;
447
448     if (resized)
449         for (i = 0; i < self->framedecors; i++) {
450             dec = &self->framedecor[i];
451             cr = &self->client->area;
452             decor_calculate_size(dec, &area);
453             if (dec->type & self->client->decorations)
454                 switch (dec->anchor) {
455                 case Decor_TopLeft:
456                     temp = area.x + area.width;
457                     if (temp > le) le = temp;
458                     temp = area.y + area.height;
459                     if (temp > te) te = temp;
460                 break;
461
462                 case Decor_Top:
463                     temp = area.y + area.height;
464                     if (temp > te) te = temp;
465                     if (area.width > cr->width) {
466                         temp = (area.width - cr->width)/2;
467                         if (temp > re) re = temp;
468                         if (temp > le) le = temp;
469                     }
470                 break;
471
472                 case Decor_TopRight:
473                     temp = area.x + area.width;
474                     if (temp > re) re = temp;
475                     temp = area.y + area.height;
476                     if (temp > te) te = temp;
477                 break;
478
479                 case Decor_Left:
480                     temp = area.x + area.width;
481                     if (temp > le) le = temp;
482                     if (area.height > cr->height) {
483                         temp = (area.height - cr->height)/2;
484                         if (temp > be) be = temp;
485                         if (temp > te) te = temp;
486                     }
487                 break;
488
489                 case Decor_Right:
490                     temp = area.x + area.width;
491                     if (temp > re) re = temp;
492                     if (area.height > cr->height) {
493                         temp = (area.height - cr->height)/2;
494                         if (temp > be) be = temp;
495                         if (temp > te) te = temp;
496                     }
497                 break;
498
499                 case Decor_BottomLeft:
500                     temp = area.x + area.width;
501                     if (temp > le) le = temp;
502                     temp = area.y + area.height;
503                     if (temp > be) be = temp;
504                 break;
505
506                 case Decor_Bottom:
507                     temp = area.y + area.height;
508                     if (temp > be) be = temp;
509                     if (area.width > cr->width) {
510                         temp = (area.width - cr->width)/2;
511                         if (temp > re) re = temp;
512                         if (temp > le) le = temp;
513                     }
514                 break;
515
516                 case Decor_BottomRight:
517                     temp = area.x + area.width;
518                     if (temp > re) re = temp;
519                     temp = area.y + area.height;
520                     if (temp > be) te = temp;
521                 break;
522             }
523         }
524 printf("frame extends by %d, %d, %d, %d\n", le, te, le, be);
525     if (resized) {
526         if (self->client->decorations & Decor_Border) {
527             self->bwidth = theme_bwidth;
528             self->cbwidth = theme_cbwidth;
529         } else {
530             self->bwidth = self->cbwidth = 0;
531         }
532         STRUT_SET(self->size, self->cbwidth + le, 
533                   self->cbwidth + te, self->cbwidth + re, self->cbwidth + be);
534
535         self->width = self->client->area.width + self->cbwidth * 2 + re + le;
536         g_assert(self->width > 0);
537     }
538     if (resized) {
539         STRUT_SET(self->size,
540                   self->cbwidth + le,
541                   self->cbwidth + te,
542                   self->cbwidth + re,
543                   self->cbwidth + be);
544
545     }
546
547     if (resized) {
548         /* move and resize the plate */
549         XMoveResizeWindow(ob_display, self->plate,
550                           self->size.left - self->cbwidth,
551                           self->size.top - self->cbwidth,
552                           self->client->area.width,
553                           self->client->area.height);
554
555         /* when the client has StaticGravity, it likes to move around. */
556         XMoveWindow(ob_display, self->client->window, 0, 0);
557     }
558
559     /* shading can change without being moved or resized */
560     RECT_SET_SIZE(self->area,
561                   self->client->area.width +
562                   self->size.left + self->size.right,
563                   (self->client->shaded ? theme_title_height + self->bwidth*2:
564                    self->client->area.height +
565                    self->size.top + self->size.bottom));
566
567     if (moved) {
568         /* find the new coordinates, done after setting the frame.size, for
569            frame_client_gravity. */
570         self->area.x = self->client->area.x;
571         self->area.y = self->client->area.y;
572         frame_client_gravity((Frame*)self,
573                              &self->area.x, &self->area.y);
574     }
575
576     if (resized)
577         for (i = 0; i < self->framedecors; i++) {
578             dec = &self->framedecor[i];
579             cr = &self->client->area;
580             decor_calculate_size(dec, &area);
581             if (!(dec->type & self->client->decorations))
582                 XUnmapWindow(ob_display, dec->window);
583             else
584                 switch (dec->anchor) {
585                     case Decor_TopLeft:
586                     x = self->size.left - area.x - area.width;
587                     y = self->size.top - area.y - area.height;
588                     XMoveResizeWindow(ob_display, dec->window, x, y,
589                                       area.width,
590                                       area.height);
591                     break;
592
593                     case Decor_Top:
594                     x = cr->width/2 + self->size.left - area.x
595                       - area.width/2;
596                     y = self->size.top - area.y - area.height;
597                     XMoveResizeWindow(ob_display, dec->window, x, y,
598                                       area.width,
599                                       area.height);
600                     break;
601
602                     case Decor_TopRight:
603                     x = self->size.left + cr->width
604                       + area.x;
605                     y = self->size.top - area.y - area.height;
606                     XMoveResizeWindow(ob_display, dec->window, x, y,
607                                       area.width,
608                                       area.height);
609                     break;
610
611                     case Decor_Left:
612                     x = self->size.left - area.x
613                       - area.width;
614                     y = cr->height/2 + self->size.top - area.y
615                       - area.height/2;
616                     XMoveResizeWindow(ob_display, dec->window, x, y,
617                                       area.width,
618                                       area.height);
619                     break;
620
621                     case Decor_Right:
622                     x = self->size.left + cr->width + area.x;
623                     y = cr->height/2 + self->size.top - area.y
624                       - area.height/2;
625                     XMoveResizeWindow(ob_display, dec->window, x, y,
626                                       area.width,
627                                       area.height);
628                     break;
629
630                     case Decor_BottomLeft:
631                     x = self->size.left - area.x - area.width;
632                     y = self->size.top + cr->height
633                       - area.y - area.height;
634                     XMoveResizeWindow(ob_display, dec->window, x, y,
635                                       area.width,
636                                       area.height);
637                     break;
638
639                     case Decor_Bottom:
640                     x = cr->width/2 + self->size.left - area.x
641                       - area.width/2;
642                     y = self->size.top + cr->height
643                       - area.y - area.height;
644                     XMoveResizeWindow(ob_display, dec->window, x, y,
645                                       area.width,
646                                       area.height);
647                     break;
648
649                     case Decor_BottomRight:
650                     x = self->size.left + cr->width + area.x;
651                     y = self->size.top + cr->height - area.y - area.height;
652                     XMoveResizeWindow(ob_display, dec->window, x, y,
653                                       area.width, area.height);
654                     break;
655                 }
656         }
657     /* move and resize the top level frame.
658        shading can change without being moved or resized */
659     XMoveResizeWindow(ob_display, self->window,
660                       self->area.x, self->area.y,
661                       self->width,
662                       self->area.height - self->bwidth * 2);
663
664     if (resized) {
665         framerender_frame(self);
666
667         frame_adjust_shape(self);
668     }
669 }