]> icculus.org git repositories - dana/openbox.git/blob - engines/concept2/frame_concept2_plugin.c
Implement plugin engine to draw frame (Follow Master patch)
[dana/openbox.git] / engines / concept2 / frame_concept2_plugin.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3  frame_default_plugin.c for the Openbox window manager
4  Copyright (c) 2006        Mikael Magnusson
5  Copyright (c) 2003-2007   Dana Jansens
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16
17  See the COPYING file for a copy of the GNU General Public License.
18  */
19
20 #include "frame_concept2_plugin.h"
21 #include "frame_concept2_render.h"
22
23 #include "openbox/frame.h"
24 #include "openbox/client.h"
25 #include "openbox/openbox.h"
26 #include "openbox/extensions.h"
27 #include "openbox/prop.h"
28 #include "openbox/grab.h"
29 #include "openbox/config.h"
30 #include "openbox/mainloop.h"
31 #include "openbox/focus_cycle.h"
32 #include "openbox/focus_cycle_indicator.h"
33 #include "openbox/moveresize.h"
34 #include "openbox/screen.h"
35 #include "render/theme.h"
36
37 typedef enum
38 {
39     OB_FLAG_MAX = 1 << 0,
40     OB_FLAG_CLOSE = 1 << 1,
41     OB_FLAG_DESK = 1 << 2,
42     OB_FLAG_SHADE = 1 << 3,
43     OB_FLAG_ICONIFY = 1 << 4
44 } ObFrameFlags;
45
46 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
47                          ButtonPressMask | ButtonReleaseMask | \
48                          SubstructureRedirectMask | FocusChangeMask)
49 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
50                            ButtonMotionMask | PointerMotionMask | \
51                            EnterWindowMask | LeaveWindowMask)
52
53 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
54 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
55
56 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
57
58 Window createWindow(Window parent, Visual *visual, gulong mask,
59         XSetWindowAttributes *attrib)
60 {
61     return XCreateWindow(plugin.ob_display, parent, 0, 0, 1, 1, 0, (visual ? 32
62             : RrDepth(plugin.ob_rr_inst)), InputOutput, (visual ? visual
63             : RrVisual(plugin.ob_rr_inst)), mask, attrib);
64
65 }
66
67 Visual *check_32bit_client(ObClient *c)
68 {
69     XWindowAttributes wattrib;
70     Status ret;
71
72     /* we're already running at 32 bit depth, yay. we don't need to use their
73      visual */
74     if (RrDepth(plugin.ob_rr_inst) == 32)
75         return NULL;
76
77     ret = XGetWindowAttributes(plugin.ob_display, c->window, &wattrib);
78     g_assert(ret != BadDrawable);
79     g_assert(ret != BadWindow);
80
81     if (wattrib.depth == 32)
82         return wattrib.visual;
83     return NULL;
84 }
85
86 /* Not used */
87 gint init(Display * display, gint screen)
88 {
89     plugin.ob_display = display;
90     plugin.ob_screen = screen;
91 }
92
93 gpointer frame_new(struct _ObClient * client)
94 {
95     XSetWindowAttributes attrib;
96     gulong mask;
97     ObConceptFrame *self;
98     Visual *visual;
99
100     self = g_new0(ObConceptFrame, 1);
101     self->client = client;
102
103     visual = check_32bit_client(client);
104
105     /* create the non-visible decor windows */
106
107     mask = 0;
108     if (visual) {
109         /* client has a 32-bit visual */
110         mask |= CWColormap | CWBackPixel | CWBorderPixel;
111         /* create a colormap with the visual */
112         OBCONCEPTFRAME(self)->colormap = attrib.colormap = XCreateColormap(
113                 plugin.ob_display, RootWindow(plugin.ob_display,
114                         plugin.ob_screen), visual, AllocNone);
115         attrib.background_pixel = BlackPixel(plugin.ob_display,
116                 plugin.ob_screen);
117         attrib.border_pixel = BlackPixel(plugin.ob_display, plugin.ob_screen);
118     }
119     self->window = createWindow(
120             RootWindow(plugin.ob_display, plugin.ob_screen), visual, mask,
121             &attrib);
122
123     /* create the visible decor windows */
124
125     mask = 0;
126     if (visual) {
127         /* client has a 32-bit visual */
128         mask |= CWColormap | CWBackPixel | CWBorderPixel;
129         attrib.colormap = RrColormap(plugin.ob_rr_inst);
130     }
131
132     self->background = createWindow(self->window, NULL, mask, &attrib);
133
134     mask |= CWEventMask;
135     attrib.event_mask = ELEMENT_EVENTMASK;
136
137     self->top = createWindow(self->window, NULL, mask, &attrib);
138     self->bottom = createWindow(self->window, NULL, mask, &attrib);
139     self->left = createWindow(self->window, NULL, mask, &attrib);
140     self->right = createWindow(self->window, NULL, mask, &attrib);
141
142     self->left_close = createWindow(self->left, NULL, mask, &attrib);
143     self->left_iconify = createWindow(self->left, NULL, mask, &attrib);
144     self->left_maximize = createWindow(self->left, NULL, mask, &attrib);
145     self->left_shade = createWindow(self->left, NULL, mask, &attrib);
146
147     self->handle = createWindow(self->left, NULL, mask, &attrib);
148
149     self->top_left = createWindow(self->window, NULL, mask, &attrib);
150     self->top_right = createWindow(self->window, NULL, mask, &attrib);
151
152     self->bottom_left = createWindow(self->window, NULL, mask, &attrib);
153     self->bottom_right = createWindow(self->window, NULL, mask, &attrib);
154
155     XMapWindow(plugin.ob_display, self->background);
156
157     self->focused = FALSE;
158
159     self->max_press = FALSE;
160     self->close_press = FALSE;
161     self->desk_press = FALSE;
162     self->iconify_press = FALSE;
163     self->shade_press = FALSE;
164     self->max_hover = FALSE;
165     self->close_hover = FALSE;
166     self->desk_hover = FALSE;
167     self->iconify_hover = FALSE;
168     self->shade_hover = FALSE;
169
170     set_theme_statics(self);
171
172     return (ObFrame*)self;
173 }
174
175 void set_theme_statics(gpointer _self)
176 {
177     ObConceptFrame * self = (ObConceptFrame *) _self;
178     /* do this before changing the frame's status like max_horz max_vert */
179
180     XResizeWindow(plugin.ob_display, self->top_left, 15,
181             theme_config.border_width);
182     XResizeWindow(plugin.ob_display, self->top_right, 15,
183             theme_config.border_width);
184     XResizeWindow(plugin.ob_display, self->bottom_left, 15,
185             theme_config.border_width);
186     XResizeWindow(plugin.ob_display, self->bottom_right, 15,
187             theme_config.border_width);
188
189     XResizeWindow(plugin.ob_display, self->left_close, theme_config.left_width,
190             15);
191     XResizeWindow(plugin.ob_display, self->left_iconify,
192             theme_config.left_width, 15);
193     XResizeWindow(plugin.ob_display, self->left_maximize,
194             theme_config.left_width, 15);
195     XResizeWindow(plugin.ob_display, self->left_shade, theme_config.left_width,
196             15);
197     XResizeWindow(plugin.ob_display, self->handle, theme_config.left_width, 15);
198
199     XMoveWindow(plugin.ob_display, self->left_close, 0, 0);
200     XMoveWindow(plugin.ob_display, self->left_iconify, 0, 15);
201     XMoveWindow(plugin.ob_display, self->left_maximize, 0, 30);
202     XMoveWindow(plugin.ob_display, self->left_shade, 0, 45);
203     XMoveWindow(plugin.ob_display, self->handle, 0, 60);
204
205 }
206
207 void free_theme_statics(gpointer _self)
208 {
209     ObConceptFrame * self = (ObConceptFrame *) _self;
210 }
211
212 void frame_free(gpointer self)
213 {
214     free_theme_statics(OBCONCEPTFRAME(self));
215     XDestroyWindow(plugin.ob_display, OBCONCEPTFRAME(self)->window);
216     if (OBCONCEPTFRAME(self)->colormap)
217         XFreeColormap(plugin.ob_display, OBCONCEPTFRAME(self)->colormap);
218     g_free(self);
219 }
220
221 void frame_show(gpointer _self)
222 {
223     ObConceptFrame * self = (ObConceptFrame *) _self;
224     if (!self->visible) {
225         self->visible = TRUE;
226         frame_update_skin(self);
227         /* Grab the server to make sure that the frame window is mapped before
228          the client gets its MapNotify, i.e. to make sure the client is
229          _visible_ when it gets MapNotify. */
230         grab_server(TRUE);
231         XMapWindow(plugin.ob_display, self->client->window);
232         XMapWindow(plugin.ob_display, self->window);
233         grab_server(FALSE);
234     }
235 }
236
237 void frame_hide(gpointer _self)
238 {
239     ObConceptFrame * self = (ObConceptFrame *) _self;
240     if (self->visible) {
241         self->visible = FALSE;
242         if (!frame_iconify_animating(self))
243             XUnmapWindow(plugin.ob_display, self->window);
244         /* we unmap the client itself so that we can get MapRequest
245          events, and because the ICCCM tells us to! */
246         XUnmapWindow(plugin.ob_display, self->client->window);
247         self->client->ignore_unmaps += 1;
248     }
249 }
250
251 void frame_adjust_theme(gpointer _self)
252 {
253     ObConceptFrame * self = (ObConceptFrame *) _self;
254     free_theme_statics(self);
255     set_theme_statics(self);
256 }
257
258 void frame_adjust_shape(gpointer _self)
259 {
260 #ifdef SHAPE
261     ObConceptFrame * self = (ObConceptFrame *) _self;
262     gint num;
263     XRectangle xrect[2];
264
265     if (!self->client->shaped)
266     {
267         /* clear the shape on the frame window */
268         XShapeCombineMask(plugin.ob_display, self->window, ShapeBounding,
269                 self->size.left,
270                 self->size.top,
271                 None, ShapeSet);
272     }
273     else
274     {
275         /* make the frame's shape match the clients */
276         XShapeCombineShape(plugin.ob_display, self->window, ShapeBounding,
277                 self->size.left,
278                 self->size.top,
279                 self->client->window,
280                 ShapeBounding, ShapeSet);
281
282         num = 0;
283         if (self->decorations)
284         {
285             xrect[0].x = 0;
286             xrect[0].y = 0;
287             xrect[0].width = self->area.width;
288             xrect[0].height = self->size.top;
289             ++num;
290         }
291
292         XShapeCombineRectangles(plugin.ob_display, self->window,
293                 ShapeBounding, 0, 0, xrect, num,
294                 ShapeUnion, Unsorted);
295     }
296 #endif
297 }
298
299 void frame_adjust_area(gpointer _self, gboolean moved, gboolean resized,
300         gboolean fake)
301 {
302     ObConceptFrame * self = (ObConceptFrame *) _self;
303
304     /* do this before changing the frame's status like max_horz max_vert */
305     frame_adjust_cursors(self);
306
307     /* Copy client status */
308     self->functions = self->client->functions;
309     self->decorations = self->client->decorations;
310     self->max_horz = self->client->max_horz;
311     self->max_vert = self->client->max_vert;
312     self->shaded = self->client->shaded;
313
314     if (self->decorations && !self->shaded) {
315         self->cbwidth_l = theme_config.left_width;
316         self->cbwidth_r = theme_config.border_width;
317         self->cbwidth_t = theme_config.border_width;
318         self->cbwidth_b = theme_config.border_width;
319
320         if (self->max_horz) {
321             self->cbwidth_l = theme_config.left_width;
322             self->cbwidth_r = 0;
323         }
324
325         if (self->max_vert) {
326             self->cbwidth_b = 0;
327             self->cbwidth_t = 0;
328         }
329
330         STRUT_SET(self->size, self->cbwidth_l, self->cbwidth_t,
331                 self->cbwidth_r, self->cbwidth_b);
332
333         RECT_SET_SIZE(self->area, self->client->area.width + self->size.left
334                 + self->size.right, self->client->area.height + self->size.top
335                 + self->size.bottom);
336
337         self->width = self->area.width;
338
339         if (!fake) {
340
341             XMoveResizeWindow(plugin.ob_display, self->top, 15, 0,
342                     self->area.width - 30, theme_config.border_width);
343             XMapWindow(plugin.ob_display, self->top);
344
345             XMoveResizeWindow(plugin.ob_display, self->bottom, 15,
346                     self->area.height - theme_config.border_width,
347                     self->area.width - 30, theme_config.border_width);
348             XMapWindow(plugin.ob_display, self->bottom);
349
350             XMoveResizeWindow(plugin.ob_display, self->left, 0,
351                     theme_config.border_width, theme_config.left_width,
352                     self->area.height - 2*theme_config.border_width);
353             XMapWindow(plugin.ob_display, self->left);
354
355             XMoveResizeWindow(plugin.ob_display, self->right, self->area.width
356                     - theme_config.border_width, theme_config.border_width,
357                     theme_config.border_width, self->area.height - 2
358                             *theme_config.border_width);
359             XMapWindow(plugin.ob_display, self->right);
360
361             XMoveWindow(plugin.ob_display, self->top_left, 0, 0);
362             XMapWindow(plugin.ob_display, self->top_left);
363             XMoveWindow(plugin.ob_display, self->top_right, self->area.width
364                     - 15, 0);
365             XMapWindow(plugin.ob_display, self->top_right);
366             XMoveWindow(plugin.ob_display, self->bottom_left, 0,
367                     self->area.height - theme_config.border_width);
368             XMapWindow(plugin.ob_display, self->bottom_left);
369             XMoveWindow(plugin.ob_display, self->bottom_right, self->area.width
370                     - 15, self->area.height - theme_config.border_width);
371             XMapWindow(plugin.ob_display, self->bottom_right);
372
373             XMapWindow(plugin.ob_display, self->left_close);
374             XMapWindow(plugin.ob_display, self->left_iconify);
375             XMapWindow(plugin.ob_display, self->left_maximize);
376             XMapWindow(plugin.ob_display, self->left_shade);
377             XMapWindow(plugin.ob_display, self->handle);
378             /* find the new coordinates, done after setting the frame.size, for
379              frame_client_gravity. */
380             self->area.x = self->client->area.x;
381             self->area.y = self->client->area.y;
382             frame_client_gravity(self, &self->area.x, &self->area.y);
383
384             XMoveResizeWindow(plugin.ob_display, self->background,
385                     theme_config.border_width, theme_config.border_width,
386                     self->area.width - 2 * theme_config.border_width,
387                     self->area.height - 2 * theme_config.border_width);
388             XMapWindow(plugin.ob_display, self->background);
389         }
390
391         XMoveWindow(plugin.ob_display, self->client->window, self->size.left,
392                 self->size.top);
393         XMoveResizeWindow(plugin.ob_display, self->window, self->area.x,
394                 self->area.y, self->area.width, self->area.height);
395
396     }
397     else if (self->shaded) {
398         self->cbwidth_l = theme_config.left_width;
399         self->cbwidth_r = 0;
400         self->cbwidth_b = 0;
401         self->cbwidth_t = 0;
402         STRUT_SET(self->size, self->cbwidth_l, self->cbwidth_t,
403                 self->cbwidth_r, self->cbwidth_b);
404
405         RECT_SET_SIZE(self->area, theme_config.left_width, self->area.height);
406
407         /* find the new coordinates, done after setting the frame.size, for
408          frame_client_gravity. */
409         self->area.x = self->client->area.x;
410         self->area.y = self->client->area.y;
411         //frame_client_gravity(self, &self->area.x, &self->area.y);
412         self->width = self->area.width;
413         if (!fake) {
414             XUnmapWindow(plugin.ob_display, self->top);
415             XUnmapWindow(plugin.ob_display, self->bottom);
416             XUnmapWindow(plugin.ob_display, self->right);
417             XUnmapWindow(plugin.ob_display, self->top_left);
418             XUnmapWindow(plugin.ob_display, self->top_right);
419             XUnmapWindow(plugin.ob_display, self->bottom_left);
420             XUnmapWindow(plugin.ob_display, self->bottom_right);
421
422             XMoveResizeWindow(plugin.ob_display, self->left, 0, 0,
423                     theme_config.left_width, self->area.height);
424             XMapWindow(plugin.ob_display, self->left);
425         }
426
427         XMoveWindow(plugin.ob_display, self->client->window,
428                 theme_config.left_width, 0);
429         XMoveResizeWindow(plugin.ob_display, self->window, self->area.x,
430                 self->area.y, self->area.width, self->area.height);
431     }
432     else {
433         self->cbwidth_l = 0;
434         self->cbwidth_r = 0;
435         self->cbwidth_b = 0;
436         self->cbwidth_t = 0;
437         STRUT_SET(self->size, self->cbwidth_l, self->cbwidth_t,
438                 self->cbwidth_r, self->cbwidth_b);
439
440         RECT_SET_SIZE(self->area, self->client->area.width + self->size.left
441                 + self->size.right, self->client->area.height + self->size.top
442                 + self->size.bottom);
443
444         /* find the new coordinates, done after setting the frame.size, for
445          frame_client_gravity. */
446         self->area.x = self->client->area.x;
447         self->area.y = self->client->area.y;
448         frame_client_gravity(self, &self->area.x, &self->area.y);
449
450         self->width = self->area.width;
451         if (!fake) {
452             XUnmapWindow(plugin.ob_display, self->top);
453             XUnmapWindow(plugin.ob_display, self->bottom);
454             XUnmapWindow(plugin.ob_display, self->left);
455             XUnmapWindow(plugin.ob_display, self->right);
456             XUnmapWindow(plugin.ob_display, self->top_left);
457             XUnmapWindow(plugin.ob_display, self->top_right);
458             XUnmapWindow(plugin.ob_display, self->bottom_left);
459             XUnmapWindow(plugin.ob_display, self->bottom_right);
460
461             XUnmapWindow(plugin.ob_display, self->handle);
462
463             XMoveResizeWindow(plugin.ob_display, self->background, 0, 0,
464                     self->area.width, self->area.height);
465             XMapWindow(plugin.ob_display, self->background);
466         }
467
468         XMoveWindow(plugin.ob_display, self->client->window, self->size.left,
469                 self->size.top);
470         XMoveResizeWindow(plugin.ob_display, self->window, self->area.x,
471                 self->area.y, self->area.width, self->area.height);
472     }
473 }
474
475 void frame_adjust_cursors(gpointer _self)
476 {
477     ObConceptFrame * self = (ObConceptFrame *) _self;
478
479     XSetWindowAttributes a;
480     a.cursor = ob_cursor(OB_CURSOR_NORTH);
481     XChangeWindowAttributes(plugin.ob_display, self->top, CWCursor, &a);
482
483     a.cursor = ob_cursor(OB_CURSOR_SOUTH);
484     XChangeWindowAttributes(plugin.ob_display, self->bottom, CWCursor, &a);
485
486     a.cursor = ob_cursor(OB_CURSOR_WEST);
487     XChangeWindowAttributes(plugin.ob_display, self->left, CWCursor, &a);
488
489     a.cursor = ob_cursor(OB_CURSOR_EAST);
490     XChangeWindowAttributes(plugin.ob_display, self->right, CWCursor, &a);
491
492     a.cursor = ob_cursor(OB_CURSOR_NORTHWEST);
493     XChangeWindowAttributes(plugin.ob_display, self->top_left, CWCursor, &a);
494
495     a.cursor = ob_cursor(OB_CURSOR_NORTHEAST);
496     XChangeWindowAttributes(plugin.ob_display, self->top_right, CWCursor, &a);
497
498     a.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
499     XChangeWindowAttributes(plugin.ob_display, self->bottom_left, CWCursor, &a);
500
501     a.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
502     XChangeWindowAttributes(plugin.ob_display, self->bottom_right, CWCursor, &a);
503
504     a.cursor = ob_cursor(OB_CURSOR_POINTER);
505     XChangeWindowAttributes(plugin.ob_display, self->left_close, CWCursor, &a);
506     XChangeWindowAttributes(plugin.ob_display, self->left_iconify, CWCursor, &a);
507     XChangeWindowAttributes(plugin.ob_display, self->left_maximize, CWCursor,
508             &a);
509     XChangeWindowAttributes(plugin.ob_display, self->left_shade, CWCursor, &a);
510     XChangeWindowAttributes(plugin.ob_display, self->handle, CWCursor, &a);
511
512 }
513
514 void frame_adjust_client_area(gpointer _self)
515 {
516     ObConceptFrame * self = (ObConceptFrame *) _self;
517     /* adjust the window which is there to prevent flashing on unmap */
518     XMoveResizeWindow(plugin.ob_display, self->background, 0, 0,
519             self->client->area.width, self->client->area.height);
520 }
521
522 void frame_adjust_state(gpointer _self)
523 {
524     ObConceptFrame * self = (ObConceptFrame *) _self;
525     self->need_render = TRUE;
526     framerender_frame(self);
527 }
528
529 void frame_adjust_focus(gpointer _self, gboolean hilite)
530 {
531     ObConceptFrame * self = (ObConceptFrame *) _self;
532     self->focused = hilite;
533     self->need_render = TRUE;
534     framerender_frame(self);
535     XFlush(plugin.ob_display);
536 }
537
538 void frame_adjust_title(gpointer _self)
539 {
540     ObConceptFrame * self = (ObConceptFrame *) _self;
541     self->need_render = TRUE;
542     framerender_frame(self);
543 }
544
545 void frame_adjust_icon(gpointer _self)
546 {
547     ObConceptFrame * self = (ObConceptFrame *) _self;
548     self->need_render = TRUE;
549     framerender_frame(self);
550 }
551
552 void frame_grab_client(gpointer _self, GHashTable * map)
553 {
554     ObConceptFrame * self = (ObConceptFrame *) _self;
555     /* DO NOT map the client window here. we used to do that, but it is bogus.
556      we need to set up the client's dimensions and everything before we
557      send a mapnotify or we create race conditions.
558      */
559
560     /* reparent the client to the frame */
561     XReparentWindow(plugin.ob_display, self->client->window, self->window, 0, 0);
562
563     /*
564      When reparenting the client window, it is usually not mapped yet, since
565      this occurs from a MapRequest. However, in the case where Openbox is
566      starting up, the window is already mapped, so we'll see an unmap event
567      for it.
568      */
569     if (ob_state() == OB_STATE_STARTING)
570         ++self->client->ignore_unmaps;
571
572     /* select the event mask on the client's parent (to receive config/map
573      req's) the ButtonPress is to catch clicks on the client border */
574     XSelectInput(plugin.ob_display, self->window, FRAME_EVENTMASK);
575
576     /* set all the windows for the frame in the window_map */
577     g_hash_table_insert(map, &self->window, self->client);
578     g_hash_table_insert(map, &self->left, self->client);
579     g_hash_table_insert(map, &self->right, self->client);
580
581     g_hash_table_insert(map, &self->top, self->client);
582     g_hash_table_insert(map, &self->bottom, self->client);
583
584     g_hash_table_insert(map, &self->top_left, self->client);
585     g_hash_table_insert(map, &self->top_right, self->client);
586
587     g_hash_table_insert(map, &self->bottom_left, self->client);
588     g_hash_table_insert(map, &self->bottom_right, self->client);
589
590     g_hash_table_insert(map, &self->left_close, self->client);
591     g_hash_table_insert(map, &self->left_iconify, self->client);
592     g_hash_table_insert(map, &self->left_maximize, self->client);
593     g_hash_table_insert(map, &self->left_shade, self->client);
594
595     g_hash_table_insert(map, &self->handle, self->client);
596
597 }
598
599 void frame_release_client(gpointer _self, GHashTable * map)
600 {
601     ObConceptFrame * self = (ObConceptFrame *) _self;
602     XEvent ev;
603     gboolean reparent = TRUE;
604
605     /* if there was any animation going on, kill it */
606     ob_main_loop_timeout_remove_data(plugin.ob_main_loop,
607             frame_animate_iconify, self, FALSE);
608
609     /* check if the app has already reparented its window away */
610     while (XCheckTypedWindowEvent(plugin.ob_display, self->client->window,
611             ReparentNotify, &ev)) {
612         /* This check makes sure we don't catch our own reparent action to
613          our frame window. This doesn't count as the app reparenting itself
614          away of course.
615
616          Reparent events that are generated by us are just discarded here.
617          They are of no consequence to us anyhow.
618          */
619         if (ev.xreparent.parent != self->window) {
620             reparent = FALSE;
621             XPutBackEvent(plugin.ob_display, &ev);
622             break;
623         }
624     }
625
626     if (reparent) {
627         /* according to the ICCCM - if the client doesn't reparent itself,
628          then we will reparent the window to root for them */
629         XReparentWindow(plugin.ob_display, self->client->window, RootWindow(
630                 plugin.ob_display, plugin.ob_screen), self->client->area.x,
631                 self->client->area.y);
632     }
633
634     /* remove all the windows for the frame from the window_map */
635     g_hash_table_remove(map, &self->window);
636
637     g_hash_table_remove(map, &self->left);
638     g_hash_table_remove(map, &self->right);
639
640     g_hash_table_remove(map, &self->top);
641     g_hash_table_remove(map, &self->bottom);
642
643     g_hash_table_remove(map, &self->top_left);
644     g_hash_table_remove(map, &self->top_right);
645
646     g_hash_table_remove(map, &self->bottom_left);
647     g_hash_table_remove(map, &self->bottom_right);
648
649     g_hash_table_remove(map, &self->left_close);
650     g_hash_table_remove(map, &self->left_iconify);
651     g_hash_table_remove(map, &self->left_maximize);
652     g_hash_table_remove(map, &self->left_shade);
653
654     g_hash_table_remove(map, &self->handle);
655
656     ob_main_loop_timeout_remove_data(plugin.ob_main_loop, flash_timeout, self,
657             TRUE);
658 }
659
660 /* is there anything present between us and the label? */
661 static gboolean is_button_present(ObConceptFrame *_self, const gchar *lc,
662         gint dir)
663 {
664     ObConceptFrame * self = (ObConceptFrame *) _self;
665     for (; *lc != '\0' && lc >= plugin.config_title_layout; lc += dir) {
666         if (*lc == ' ')
667             continue; /* it was invalid */
668         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
669             return TRUE;
670         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
671             return TRUE;
672         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
673             return TRUE;
674         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
675             return TRUE;
676         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
677             return TRUE;
678         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
679             return TRUE;
680         if (*lc == 'L')
681             return FALSE;
682     }
683     return FALSE;
684 }
685
686 ObFrameContext frame_context(gpointer _self, Window win, gint x, gint y)
687 {
688     /* Here because client can be NULL */
689     ObConceptFrame *self = OBCONCEPTFRAME(_self);
690
691     if (win == self->window)
692         return OB_FRAME_CONTEXT_FRAME;
693
694     if (win == self->bottom)
695         return OB_FRAME_CONTEXT_BOTTOM;
696
697     if (win == self->bottom_left)
698         return OB_FRAME_CONTEXT_BLCORNER;
699
700     if (win == self->bottom_right)
701         return OB_FRAME_CONTEXT_BRCORNER;
702
703     if (win == self->top)
704         return OB_FRAME_CONTEXT_TOP;
705
706     if (win == self->top_left)
707         return OB_FRAME_CONTEXT_TLCORNER;
708
709     if (win == self->top_right)
710         return OB_FRAME_CONTEXT_TRCORNER;
711
712     if (win == self->left_close)
713         return OB_FRAME_CONTEXT_CLOSE;
714     if (win == self->left_iconify)
715         return OB_FRAME_CONTEXT_ICONIFY;
716     if (win == self->left_maximize)
717         return OB_FRAME_CONTEXT_MAXIMIZE;
718     if (win == self->left_shade)
719         return OB_FRAME_CONTEXT_SHADE;
720     if (win == self->handle)
721         return OB_FRAME_CONTEXT_TITLEBAR;
722
723     if (win == self->left)
724         return OB_FRAME_CONTEXT_LEFT;
725     if (win == self->right)
726         return OB_FRAME_CONTEXT_RIGHT;
727
728     return OB_FRAME_CONTEXT_NONE;
729 }
730
731 void frame_client_gravity(gpointer _self, gint *x, gint *y)
732 {
733     ObConceptFrame * self = OBCONCEPTFRAME(_self);
734     /* horizontal */
735     switch (self->client->gravity) {
736     default:
737     case NorthWestGravity:
738     case SouthWestGravity:
739     case WestGravity:
740         break;
741
742     case NorthGravity:
743     case SouthGravity:
744     case CenterGravity:
745         /* the middle of the client will be the middle of the frame */
746         *x -= (self->size.right - self->size.left) / 2;
747         break;
748
749     case NorthEastGravity:
750     case SouthEastGravity:
751     case EastGravity:
752         /* the right side of the client will be the right side of the frame */
753         *x -= self->size.right + self->size.left - self->client->border_width
754                 * 2;
755         break;
756
757     case ForgetGravity:
758     case StaticGravity:
759         /* the client's position won't move */
760         *x -= self->size.left - self->client->border_width;
761         break;
762     }
763
764     /* vertical */
765     switch (self->client->gravity) {
766     default:
767     case NorthWestGravity:
768     case NorthEastGravity:
769     case NorthGravity:
770         break;
771
772     case CenterGravity:
773     case EastGravity:
774     case WestGravity:
775         /* the middle of the client will be the middle of the frame */
776         *y -= (self->size.bottom - self->size.top) / 2;
777         break;
778
779     case SouthWestGravity:
780     case SouthEastGravity:
781     case SouthGravity:
782         /* the bottom of the client will be the bottom of the frame */
783         *y -= self->size.bottom + self->size.top - self->client->border_width
784                 * 2;
785         break;
786
787     case ForgetGravity:
788     case StaticGravity:
789         /* the client's position won't move */
790         *y -= self->size.top - self->client->border_width;
791         break;
792     }
793 }
794
795 void frame_frame_gravity(gpointer _self, gint *x, gint *y)
796 {
797     ObConceptFrame * self = (ObConceptFrame *) _self;
798     /* horizontal */
799     switch (self->client->gravity) {
800     default:
801     case NorthWestGravity:
802     case WestGravity:
803     case SouthWestGravity:
804         break;
805     case NorthGravity:
806     case CenterGravity:
807     case SouthGravity:
808         /* the middle of the client will be the middle of the frame */
809         *x += (self->size.right - self->size.left) / 2;
810         break;
811     case NorthEastGravity:
812     case EastGravity:
813     case SouthEastGravity:
814         /* the right side of the client will be the right side of the frame */
815         *x += self->size.right + self->size.left - self->client->border_width
816                 * 2;
817         break;
818     case StaticGravity:
819     case ForgetGravity:
820         /* the client's position won't move */
821         *x += self->size.left - self->client->border_width;
822         break;
823     }
824
825     /* vertical */
826     switch (self->client->gravity) {
827     default:
828     case NorthWestGravity:
829     case NorthGravity:
830     case NorthEastGravity:
831         break;
832     case WestGravity:
833     case CenterGravity:
834     case EastGravity:
835         /* the middle of the client will be the middle of the frame */
836         *y += (self->size.bottom - self->size.top) / 2;
837         break;
838     case SouthWestGravity:
839     case SouthGravity:
840     case SouthEastGravity:
841         /* the bottom of the client will be the bottom of the frame */
842         *y += self->size.bottom + self->size.top - self->client->border_width
843                 * 2;
844         break;
845     case StaticGravity:
846     case ForgetGravity:
847         /* the client's position won't move */
848         *y += self->size.top - self->client->border_width;
849         break;
850     }
851 }
852
853 void frame_rect_to_frame(gpointer _self, Rect *r)
854 {
855     ObConceptFrame * self = (ObConceptFrame *) _self;
856     r->width += self->size.left + self->size.right;
857     r->height += self->size.top + self->size.bottom;
858     frame_client_gravity(self, &r->x, &r->y);
859 }
860
861 void frame_rect_to_client(gpointer _self, Rect *r)
862 {
863     ObConceptFrame * self = (ObConceptFrame *) _self;
864     r->width -= self->size.left + self->size.right;
865     r->height -= self->size.top + self->size.bottom;
866     frame_frame_gravity(self, &r->x, &r->y);
867 }
868
869 void flash_done(gpointer data)
870 {
871     ObConceptFrame *self = data;
872
873     if (self->focused != self->flash_on)
874         frame_adjust_focus(self, self->focused);
875 }
876
877 gboolean flash_timeout(gpointer data)
878 {
879     ObConceptFrame *self = data;
880     GTimeVal now;
881
882     g_get_current_time(&now);
883     if (now.tv_sec > self->flash_end.tv_sec
884             || (now.tv_sec == self->flash_end.tv_sec && now.tv_usec
885                     >= self->flash_end.tv_usec))
886         self->flashing = FALSE;
887
888     if (!self->flashing)
889         return FALSE; /* we are done */
890
891     self->flash_on = !self->flash_on;
892     if (!self->focused) {
893         frame_adjust_focus(self, self->flash_on);
894         self->focused = FALSE;
895     }
896
897     return TRUE; /* go again */
898 }
899
900 void frame_flash_start(gpointer _self)
901 {
902     ObConceptFrame * self = (ObConceptFrame *) _self;
903     self->flash_on = self->focused;
904
905     if (!self->flashing)
906         ob_main_loop_timeout_add(plugin.ob_main_loop, G_USEC_PER_SEC * 0.6,
907                 flash_timeout, self, g_direct_equal, flash_done);
908     g_get_current_time(&self->flash_end);
909     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
910
911     self->flashing = TRUE;
912 }
913
914 void frame_flash_stop(gpointer _self)
915 {
916     ObConceptFrame * self = (ObConceptFrame *) _self;
917     self->flashing = FALSE;
918 }
919
920 static gulong frame_animate_iconify_time_left(gpointer _self,
921         const GTimeVal *now)
922 {
923     ObConceptFrame * self = (ObConceptFrame *) _self;
924     glong sec, usec;
925     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
926     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
927     if (usec < 0) {
928         usec += G_USEC_PER_SEC;
929         sec--;
930     }
931     /* no negative values */
932     return MAX(sec * G_USEC_PER_SEC + usec, 0);
933 }
934
935 gboolean frame_animate_iconify(gpointer p)
936 {
937     ObConceptFrame *self = p;
938     gint x, y, w, h;
939     gint iconx, icony, iconw;
940     GTimeVal now;
941     gulong time;
942     gboolean iconifying;
943
944     if (self->client->icon_geometry.width == 0) {
945         /* there is no icon geometry set so just go straight down */
946         Rect *a =
947                 screen_physical_area_monitor(screen_find_monitor(&self->area));
948         iconx = self->area.x + self->area.width / 2 + 32;
949         icony = a->y + a->width;
950         iconw = 64;
951         g_free(a);
952     }
953     else {
954         iconx = self->client->icon_geometry.x;
955         icony = self->client->icon_geometry.y;
956         iconw = self->client->icon_geometry.width;
957     }
958
959     iconifying = self->iconify_animation_going > 0;
960
961     /* how far do we have left to go ? */
962     g_get_current_time(&now);
963     time = frame_animate_iconify_time_left(self, &now);
964
965     if (time == 0 || iconifying) {
966         /* start where the frame is supposed to be */
967         x = self->area.x;
968         y = self->area.y;
969         w = self->area.width;
970         h = self->area.height;
971     }
972     else {
973         /* start at the icon */
974         x = iconx;
975         y = icony;
976         w = iconw;
977         h = self->size.top; /* just the titlebar */
978     }
979
980     if (time > 0) {
981         glong dx, dy, dw;
982         glong elapsed;
983
984         dx = self->area.x - iconx;
985         dy = self->area.y - icony;
986         dw = self->area.width - self->bwidth * 2 - iconw;
987         /* if restoring, we move in the opposite direction */
988         if (!iconifying) {
989             dx = -dx;
990             dy = -dy;
991             dw = -dw;
992         }
993
994         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
995         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
996         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
997         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
998         h = self->size.top; /* just the titlebar */
999     }
1000
1001     if (time == 0)
1002         frame_end_iconify_animation(self);
1003     else {
1004         XMoveResizeWindow(plugin.ob_display, self->window, x, y, w, h);
1005         XFlush(plugin.ob_display);
1006     }
1007
1008     return time > 0; /* repeat until we're out of time */
1009 }
1010
1011 void frame_end_iconify_animation(gpointer _self)
1012 {
1013     ObConceptFrame * self = (ObConceptFrame *) _self;
1014     /* see if there is an animation going */
1015     if (self->iconify_animation_going == 0)
1016         return;
1017
1018     if (!self->visible)
1019         XUnmapWindow(plugin.ob_display, self->window);
1020     else {
1021         /* Send a ConfigureNotify when the animation is done, this fixes
1022          KDE's pager showing the window in the wrong place.  since the
1023          window is mapped at a different location and is then moved, we
1024          need to send the synthetic configurenotify, since apps may have
1025          read the position when the client mapped, apparently. */
1026         client_reconfigure(self->client, TRUE);
1027     }
1028
1029     /* we're not animating any more ! */
1030     self->iconify_animation_going = 0;
1031
1032     XMoveResizeWindow(plugin.ob_display, self->window, self->area.x,
1033             self->area.y, self->area.width, self->area.height);
1034     /* we delay re-rendering until after we're done animating */
1035     framerender_frame(self);
1036     XFlush(plugin.ob_display);
1037 }
1038
1039 void frame_begin_iconify_animation(gpointer _self, gboolean iconifying)
1040 {
1041     ObConceptFrame * self = (ObConceptFrame *) _self;
1042     gulong time;
1043     gboolean new_anim = FALSE;
1044     gboolean set_end = TRUE;
1045     GTimeVal now;
1046
1047     /* if there is no titlebar, just don't animate for now
1048      XXX it would be nice tho.. */
1049     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1050         return;
1051
1052     /* get the current time */
1053     g_get_current_time(&now);
1054
1055     /* get how long until the end */
1056     time = FRAME_ANIMATE_ICONIFY_TIME;
1057     if (self->iconify_animation_going) {
1058         if (!!iconifying != (self->iconify_animation_going > 0)) {
1059             /* animation was already going on in the opposite direction */
1060             time = time - frame_animate_iconify_time_left(self, &now);
1061         }
1062         else
1063             /* animation was already going in the same direction */
1064             set_end = FALSE;
1065     }
1066     else
1067         new_anim = TRUE;
1068     self->iconify_animation_going = iconifying ? 1 : -1;
1069
1070     /* set the ending time */
1071     if (set_end) {
1072         self->iconify_animation_end.tv_sec = now.tv_sec;
1073         self->iconify_animation_end.tv_usec = now.tv_usec;
1074         g_time_val_add(&self->iconify_animation_end, time);
1075     }
1076
1077     if (new_anim) {
1078         ob_main_loop_timeout_remove_data(plugin.ob_main_loop,
1079                 frame_animate_iconify, self, FALSE);
1080         ob_main_loop_timeout_add(plugin.ob_main_loop,
1081         FRAME_ANIMATE_ICONIFY_STEP_TIME, frame_animate_iconify, self,
1082                 g_direct_equal, NULL);
1083
1084         /* do the first step */
1085         frame_animate_iconify(self);
1086
1087         /* show it during the animation even if it is not "visible" */
1088         if (!self->visible)
1089             XMapWindow(plugin.ob_display, self->window);
1090     }
1091 }
1092
1093 gboolean frame_iconify_animating(gpointer _self)
1094 {
1095     ObConceptFrame * self = (ObConceptFrame *) _self;
1096     return self->iconify_animation_going != 0;
1097 }
1098
1099 ObFramePlugin plugin = { 0, //gpointer handler;
1100         "libdefault.la", //gchar * filename;
1101         "Default", //gchar * name;
1102         init, //gint (*init) (Display * display, gint screen);
1103         0, // release
1104         frame_new, //gpointer (*frame_new) (struct _ObClient *c);
1105         frame_free, //void (*frame_free) (gpointer self);
1106         frame_show, //void (*frame_show) (gpointer self);
1107         frame_hide, //void (*frame_hide) (gpointer self);
1108         frame_adjust_theme, //void (*frame_adjust_theme) (gpointer self);
1109         frame_adjust_shape, //void (*frame_adjust_shape) (gpointer self);
1110         frame_adjust_area, //void (*frame_adjust_area) (gpointer self, gboolean moved, gboolean resized, gboolean fake);
1111         frame_adjust_client_area, //void (*frame_adjust_client_area) (gpointer self);
1112         frame_adjust_state, //void (*frame_adjust_state) (gpointer self);
1113         frame_adjust_focus, //void (*frame_adjust_focus) (gpointer self, gboolean hilite);
1114         frame_adjust_title, //void (*frame_adjust_title) (gpointer self);
1115         frame_adjust_icon, //void (*frame_adjust_icon) (gpointer self);
1116         frame_grab_client, //void (*frame_grab_client) (gpointer self);
1117         frame_release_client, //void (*frame_release_client) (gpointer self);
1118         frame_context, //ObFrameContext (*frame_context) (struct _ObClient *self, Window win, gint x, gint y);
1119         frame_client_gravity, //void (*frame_client_gravity) (gpointer self, gint *x, gint *y);
1120         frame_frame_gravity, //void (*frame_frame_gravity) (gpointer self, gint *x, gint *y);
1121         frame_rect_to_frame, //void (*frame_rect_to_frame) (gpointer self, Rect *r);
1122         frame_rect_to_client, //void (*frame_rect_to_client) (gpointer self, Rect *r);
1123         frame_flash_start, //void (*frame_flash_start) (gpointer self);
1124         frame_flash_stop, //void (*frame_flash_stop) (gpointer self);
1125         frame_begin_iconify_animation, //void (*frame_begin_iconify_animation) (gpointer self, gboolean iconifying);
1126         frame_end_iconify_animation, //void (*frame_end_iconify_animation) (gpointer self);
1127         frame_iconify_animating, // gboolean (*frame_iconify_animating)(gpointer p);
1128         load_theme_config,
1129
1130         /* This fields are fill by openbox. */
1131         0, //Display * ob_display;
1132         0, //gint ob_screen;
1133         0, //RrInstance *ob_rr_inst;
1134         0, //gboolean config_theme_keepborder;
1135         0, //struct _ObClient *focus_cycle_target;
1136         0, //gchar *config_title_layout;
1137         FALSE, //gboolean moveresize_in_progress;
1138         0, //struct _ObMainLoop *ob_main_loop;
1139 };
1140
1141 ObFramePlugin * get_info()
1142 {
1143     return &plugin;
1144 }