-#include "slit.h"
-#include "screen.h"
-#include "grab.h"
-#include "openbox.h"
-#include "render/theme.h"
-
-#define SLIT_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
- EnterWindowMask | LeaveWindowMask)
-#define SLITAPP_EVENT_MASK (StructureNotifyMask)
-
-GHashTable *slit_map = NULL;
-GHashTable *slit_app_map = NULL;
-
-static Slit *slit;
-static int nslits;
-
-static guint slit_hide_timeout = 3000; /* XXX make a config option */
-
-static void slit_configure(Slit *self);
-
-void slit_startup()
-{
- XSetWindowAttributes attrib;
- int i;
-
- slit_map = g_hash_table_new(g_int_hash, g_int_equal);
- slit_app_map = g_hash_table_new(g_int_hash, g_int_equal);
-
- nslits = 1;
- slit = g_new0(struct Slit, nslits);
- slit->obwin.type = Window_Slit;
-
- for (i = 0; i < nslits; ++i) {
- slit[i].horz = FALSE;
- slit[i].hide = FALSE;
- slit[i].hidden = TRUE;
- slit[i].pos = SlitPos_TopRight;
- slit[i].layer = Layer_Top;
-
- attrib.event_mask = SLIT_EVENT_MASK;
- attrib.override_redirect = True;
- slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
- render_depth, InputOutput, render_visual,
- CWOverrideRedirect | CWEventMask,
- &attrib);
- slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
- XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
- XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
-
- g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
- stacking_add(SLIT_AS_WINDOW(&slit[i]));
- stacking_raise(SLIT_AS_WINDOW(&slit[i]));
- }
-}
-
-void slit_shutdown()
-{
- int i;
-
- for (i = 0; i < nslits; ++i) {
- XDestroyWindow(ob_display, slit[i].frame);
- appearance_free(slit[i].a_frame);
- g_hash_table_remove(slit_map, &slit[i].frame);
- stacking_remove(&slit[i]);
- }
- g_hash_table_destroy(slit_app_map);
- g_hash_table_destroy(slit_map);
-}
-
-void slit_add(Window win, XWMHints *wmhints)
-{
- Slit *s;
- SlitApp *app;
- XWindowAttributes attrib;
-
- /* XXX pick a slit */
- s = &slit[0];
-
- app = g_new0(SlitApp, 1);
- app->slit = s;
- app->win = win;
- app->icon_win = (wmhints->flags & IconWindowHint) ?
- wmhints->icon_window : win;
-
- if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
- app->w = attrib.width;
- app->h = attrib.height;
- } else {
- app->w = app->h = 64;
- }
-
- s->slit_apps = g_list_append(s->slit_apps, app);
- slit_configure(s);
-
- XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
- /*
- This is the same case as in frame.c for client windows. When Openbox is
- starting, the window is already mapped so we see unmap events occur for
- it. There are 2 unmap events generated that we see, one with the 'event'
- member set the root window, and one set to the client, but both get
- handled and need to be ignored.
- */
- if (ob_state == State_Starting)
- app->ignore_unmaps += 2;
-
- if (app->win != app->icon_win) {
- /* have to map it so that it can be re-managed on a restart */
- XMoveWindow(ob_display, app->win, -1000, -1000);
- XMapWindow(ob_display, app->win);
- }
- XMapWindow(ob_display, app->icon_win);
- XSync(ob_display, False);
-
- /* specify that if we exit, the window should not be destroyed and should
- be reparented back to root automatically */
- XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
- XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK);
-
- grab_button_full(2, 0, app->icon_win,
- ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
- GrabModeAsync, ob_cursors.move);
-
- g_hash_table_insert(slit_app_map, &app->icon_win, app);
-
- g_message("Managed Slit App: 0x%lx", app->icon_win);
-}
-
-void slit_remove_all()
-{
- int i;
-
- for (i = 0; i < nslits; ++i)
- while (slit[i].slit_apps)
- slit_remove(slit[i].slit_apps->data, TRUE);
-}
-
-void slit_remove(SlitApp *app, gboolean reparent)
-{
- ungrab_button(2, 0, app->icon_win);
- XSelectInput(ob_display, app->icon_win, NoEventMask);
- /* remove the window from our save set */
- XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
- XSync(ob_display, False);
-
- g_hash_table_remove(slit_app_map, &app->icon_win);
-
- if (reparent)
- XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
-
- app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
- slit_configure(app->slit);
-
- g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
-
- g_free(app);
-}
-
-void slit_configure_all()
-{
- int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
-}
-
-static void slit_configure(Slit *self)
-{
- GList *it;
- int spot;
-
- self->w = self->h = spot = 0;
-
- for (it = self->slit_apps; it; it = it->next) {
- struct SlitApp *app = it->data;
- if (self->horz) {
- app->x = spot;
- app->y = 0;
- self->w += app->w;
- self->h = MAX(self->h, app->h);
- spot += app->w;
- } else {
- app->x = 0;
- app->y = spot;
- self->w = MAX(self->w, app->w);
- self->h += app->h;
- spot += app->h;
- }
-
- XMoveWindow(ob_display, app->icon_win, app->x, app->y);
- }
-
- /* used for calculating offsets */
- self->w += theme_bwidth * 2;
- self->h += theme_bwidth * 2;
-
- /* calculate position */
- switch (self->pos) {
- case SlitPos_Floating:
- self->x = self->user_x;
- self->y = self->user_y;
- break;
- case SlitPos_TopLeft:
- self->x = 0;
- self->y = 0;
- self->gravity = NorthWestGravity;
- break;
- case SlitPos_Top:
- self->x = screen_physical_size.width / 2;
- self->y = 0;
- self->gravity = NorthGravity;
- break;
- case SlitPos_TopRight:
- self->x = screen_physical_size.width;
- self->y = 0;
- self->gravity = NorthEastGravity;
- break;
- case SlitPos_Left:
- self->x = 0;
- self->y = screen_physical_size.height / 2;
- self->gravity = WestGravity;
- break;
- case SlitPos_Right:
- self->x = screen_physical_size.width;
- self->y = screen_physical_size.height / 2;
- self->gravity = EastGravity;
- break;
- case SlitPos_BottomLeft:
- self->x = 0;
- self->y = screen_physical_size.height;
- self->gravity = SouthWestGravity;
- break;
- case SlitPos_Bottom:
- self->x = screen_physical_size.width / 2;
- self->y = screen_physical_size.height;
- self->gravity = SouthGravity;
- break;
- case SlitPos_BottomRight:
- self->x = screen_physical_size.width;
- self->y = screen_physical_size.height;
- self->gravity = SouthEastGravity;
- break;
- }
-
- switch(self->gravity) {
- case NorthGravity:
- case CenterGravity:
- case SouthGravity:
- self->x -= self->w / 2;
- break;
- case NorthEastGravity:
- case EastGravity:
- case SouthEastGravity:
- self->x -= self->w;
- break;
- }
- switch(self->gravity) {
- case WestGravity:
- case CenterGravity:
- case EastGravity:
- self->y -= self->h / 2;
- break;
- case SouthWestGravity:
- case SouthGravity:
- case SouthEastGravity:
- self->y -= self->h;
- break;
- }
-
- if (self->hide && self->hidden) {
- g_message("hidden");
- switch (self->pos) {
- case SlitPos_Floating:
- break;
- case SlitPos_TopLeft:
- if (self->horz)
- self->y -= self->h - theme_bwidth;
- else
- self->x -= self->w - theme_bwidth;
- break;
- case SlitPos_Top:
- self->y -= self->h - theme_bwidth;
- break;
- case SlitPos_TopRight:
- if (self->horz)
- self->y -= self->h - theme_bwidth;
- else
- self->x += self->w - theme_bwidth;
- break;
- case SlitPos_Left:
- self->x -= self->w - theme_bwidth;
- break;
- case SlitPos_Right:
- self->x += self->w - theme_bwidth;
- break;
- case SlitPos_BottomLeft:
- if (self->horz)
- self->y += self->h - theme_bwidth;
- else
- self->x -= self->w - theme_bwidth;
- break;
- case SlitPos_Bottom:
- self->y += self->h - theme_bwidth;
- break;
- case SlitPos_BottomRight:
- if (self->horz)
- self->y += self->h - theme_bwidth;
- else
- self->x += self->w - theme_bwidth;
- break;
- }
- }
-
- /* not used for actually sizing shit */
- self->w -= theme_bwidth * 2;
- self->h -= theme_bwidth * 2;
-
- if (self->w > 0 && self->h > 0) {
- RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
- XMoveResizeWindow(ob_display, self->frame,
- self->x, self->y, self->w, self->h);
-
- paint(self->frame, self->a_frame);
- XMapWindow(ob_display, self->frame);
- } else
- XUnmapWindow(ob_display, self->frame);
-
- /* but they are useful outside of this function! */
- self->w += theme_bwidth * 2;
- self->h += theme_bwidth * 2;
-}
-
-void slit_app_configure(SlitApp *app, int w, int h)
-{
- app->w = w;
- app->h = h;
- slit_configure(app->slit);
-}
-
-void slit_app_drag(SlitApp *app, XMotionEvent *e)
-{
- Slit *src, *dest = NULL;
- SlitApp *over = NULL;
- GList *it;
- int i;
- int x, y;
- gboolean after;
-
- src = app->slit;
- x = e->x_root;
- y = e->y_root;
-
- /* which slit are we on top of? */
- for (i = 0; i < nslits; ++i)
- if (x >= slit[i].x &&
- y >= slit[i].y &&
- x < slit[i].x + slit[i].w &&
- y < slit[i].y + slit[i].h) {
- dest = &slit[i];
- break;
- }
- if (!dest) return;
-
- x -= dest->x;
- y -= dest->y;
-
- /* which slit app are we on top of? */
- for (it = dest->slit_apps; it; it = it->next) {
- over = it->data;
- if (dest->horz) {
- if (x >= over->x && x < over->x + over->w)
- break;
- } else {
- if (y >= over->y && y < over->y + over->h)
- break;
- }
- }
- if (!it || app == over) return;
-
- x -= over->x;
- y -= over->y;
-
- if (dest->horz)
- after = (x > over->w / 2);
- else
- after = (y > over->h / 2);
-
- /* remove before doing the it->next! */
- src->slit_apps = g_list_remove(src->slit_apps, app);
- if (src != dest) slit_configure(src);
-
- if (after) it = it->next;
-
- dest->slit_apps = g_list_insert_before(dest->slit_apps, it, app);
- slit_configure(dest);
-}
-
-static void hide_timeout(Slit *self)
-{
- /* dont repeat */
- timer_stop(self->hide_timer);
- self->hide_timer = NULL;
-
- /* hide */
- self->hidden = TRUE;
- slit_configure(self);
-}
-
-void slit_hide(Slit *self, gboolean hide)
-{
- if (self->hidden == hide || !self->hide)
- return;
- if (!hide) {
- /* show */
- self->hidden = FALSE;
- slit_configure(self);
-
- /* if was hiding, stop it */
- if (self->hide_timer) {
- timer_stop(self->hide_timer);
- self->hide_timer = NULL;
- }
- } else {
- g_assert(!self->hide_timer);
- self->hide_timer = timer_start(slit_hide_timeout * 1000,
- (TimeoutHandler)hide_timeout, self);
- }
-}