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