diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/msg.c | 670 | ||||
-rw-r--r-- | src/msg.h | 20 | ||||
-rw-r--r-- | src/panel.c | 2030 | ||||
-rw-r--r-- | src/panel.h | 312 | ||||
-rw-r--r-- | src/panel_app.c | 597 | ||||
-rw-r--r-- | src/panel_app.h | 92 | ||||
-rw-r--r-- | src/panel_menu.c | 276 | ||||
-rw-r--r-- | src/panel_menu.h | 27 | ||||
-rw-r--r-- | src/panel_util.c | 233 | ||||
-rw-r--r-- | src/panel_util.h | 33 | ||||
-rw-r--r-- | src/session.c | 311 | ||||
-rw-r--r-- | src/session.h | 37 |
13 files changed, 4653 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..92f9f65 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,15 @@ +PREFIXDIR = $(prefix) +PKGDATADIR = $(pkgdatadir) +DATADIR = $(datadir) + +INCLUDES = -DDATADIR=\"$(DATADIR)\" -DPKGDATADIR=\"$(PKGDATADIR)\" -DPREFIX=\"$(PREFIXDIR)\" $(LIBMB_CFLAGS) + +bin_PROGRAMS = matchbox-panel + +matchbox_panel_LDADD = $(LIBMB_LIBS) + +matchbox_panel_SOURCES = \ + panel.c panel_app.c panel_menu.c panel_util.c session.c msg.c \ + panel.h panel_app.h panel_menu.h panel_util.h session.h msg.h + + diff --git a/src/msg.c b/src/msg.c new file mode 100644 index 0000000..f213c95 --- /dev/null +++ b/src/msg.c @@ -0,0 +1,670 @@ +#include "msg.h" + +static MBLayout* +msg_calc_win_size(MBPanel *d, MBPanelMessageQueue *m, int *w, int *h); + + +/* +static int +_get_text_length(MBPanel *d, char *str, int cnt) +{ +#ifdef USE_XFT + XGlyphInfo extents; + XftTextExtents8(d->dpy, d->msg_font, (unsigned char *) str, + cnt, &extents); + return extents.width; +#else + return XTextWidth(d->msg_font, str, cnt); +#endif +} +*/ + +static unsigned long +_get_server_time(MBPanel *d) +{ + XEvent xevent; + char c = 'a'; + + XChangeProperty (d->dpy, d->win_root, + d->atoms[ATOM_MB_DOCK_TIMESTAMP], + d->atoms[ATOM_MB_DOCK_TIMESTAMP], + 8, PropModeReplace, &c, 1); + + for (;;) { + XMaskEvent(d->dpy, PropertyChangeMask, &xevent); + if (xevent.xproperty.atom == d->atoms[ATOM_MB_DOCK_TIMESTAMP]) + { + return xevent.xproperty.time; + } + } +} + +static void +_send_tray_context_message(MBPanel *panel, + Window win) +{ + XEvent ev; + + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = win; + ev.xclient.message_type = panel->atoms[ATOM_MB_SYSTEM_TRAY_CONTEXT]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = _get_server_time(panel); + + XSendEvent(panel->dpy, win, False, NoEventMask, &ev); + XSync(panel->dpy, False); +} + + +MBPanelMessageQueue* +msg_new(MBPanel *dock, XClientMessageEvent *e) +{ + MBPanelMessageQueue *m; + MBPanelApp *sender; + + if ((sender = panel_app_get_from_window(dock, e->window )) == NULL) + return NULL; + + m = (MBPanelMessageQueue *)malloc(sizeof(MBPanelMessageQueue)); + m->sender = sender; + + m->starttime = e->data.l[0]; + m->timeout = e->data.l[2]; + + m->total_msg_length = e->data.l[3]; + m->id = e->data.l[4]; + m->data = (unsigned char *)malloc(sizeof(unsigned char) + *(m->total_msg_length+1)); + m->current_msg_length = 0; + m->pending = False; + m->next = NULL; + + if (dock->msg_queue_start == NULL) + { + DBG("queue is empty\n"); + dock->msg_queue_start = dock->msg_queue_end = m; + } else { + DBG("queue is not empty\n"); + dock->msg_queue_end->next = m; + dock->msg_queue_end = m; + } + + return m; +} + +void +msg_destroy(MBPanel *panel, MBPanelMessageQueue *msg) +{ + MBPanelMessageQueue *msg_q; + if (panel->msg_queue_start == msg) + { + panel->msg_queue_start = msg->next; + } else { + for(msg_q=panel->msg_queue_start; msg_q->next != NULL; msg_q=msg_q->next) + { + if (msg_q->next == msg) + { + msg_q->next = msg->next; + break; + } + } + } + + if (msg->extra_context_data) XFree(msg->extra_context_data); + free(msg->data); + free(msg); +} + +void +msg_add_data(MBPanel *d, XClientMessageEvent *e) +{ + MBPanelMessageQueue *m; + + for(m=d->msg_queue_start; m != NULL; m=m->next) + { + if (m->sender->win == e->window) + { + if ( (m->total_msg_length - m->current_msg_length) > 20) + { + memcpy(&m->data[m->current_msg_length],e->data.b,sizeof(char)*20); + m->current_msg_length += 20; + } else { + memcpy(&m->data[m->current_msg_length],e->data.b, + (m->total_msg_length-m->current_msg_length) ); + m->current_msg_length = m->total_msg_length; + m->data[m->total_msg_length] = '\0'; + + m->pending = True; + } + return; + } + } +} + +void +msg_win_create(MBPanel *panel, + MBPanelMessageQueue *msg) +{ + int msg_win_x = 0, msg_win_y = 0, msg_win_w = 0, msg_win_h = 0, + box_x_offset = 0, box_y_offset = 0, arrow_offset = 0, cnt = 0, + box_w, box_h; + + int x, y, txt_v_offset; + unsigned char r, g, b, fr, fg, fb; + + MBPixbufImage *img_backing = NULL; + MBDrawable *tmp_drw; + + Pixmap mask; + GC mask_gc; + XSetWindowAttributes attr; + XWindowAttributes root_attr; + XWMHints *wm_hints; + long winmask; + MBLayout *layout; + + /* +#ifdef USE_XFT + XftDraw *xftdraw; + XftColor txt_xftcol; + XRenderColor colortmp; +#endif + */ + + layout = msg_calc_win_size(panel, msg, &msg_win_w, &msg_win_h); + + msg_win_h += (2*MSG_WIN_Y_PAD); + + box_w = msg_win_w; box_h = msg_win_h; + + XGetWindowAttributes(panel->dpy, panel->win_root, &root_attr); + +#define ARROW_SIZE 4 +#define MSG_TEXT_MARGIN 10 + + switch (panel->orientation) + { + case East: + msg_win_w += ARROW_SIZE; + msg_win_x = panel->x - msg_win_w - 5; + break; + case West: + msg_win_w += ARROW_SIZE; + box_x_offset += ARROW_SIZE; + msg_win_x = panel->x + panel->w + 5; + break; + case South: + msg_win_h += ARROW_SIZE; + msg_win_y = panel->y - msg_win_h - 5; + break; + case North: + msg_win_h += ARROW_SIZE; + box_y_offset += ARROW_SIZE; + msg_win_y = panel->y + panel->h + 5; + break; + } + + printf("%s() called()\n", __func__); + + if (PANEL_IS_VERTICAL(panel)) + { + arrow_offset = msg_win_h / 2; + msg_win_y = panel->y + msg->sender->y + (msg->sender->h/2) - arrow_offset; + + if (msg_win_y < 0) + { + msg_win_y = 5; /* a little margin to display top */ + arrow_offset = msg->sender->y + (msg->sender->h/2); + } + + if (msg_win_y + msg_win_h > panel->y + panel->h) + { + /* reposition with a bit of margin - 5px */ + msg_win_y = (panel->y + panel->h) - msg_win_h - 5; + arrow_offset = panel->y + msg->sender->y - msg_win_y + (msg->sender->h/2); + } + } + else + { + arrow_offset = msg_win_w / 2; + msg_win_x = panel->x + msg->sender->x + (msg->sender->w/2) - arrow_offset; + + if (msg_win_x < 0) + { + msg_win_x = 5; + arrow_offset = msg->sender->x + (msg->sender->w/2); + } + + if (msg_win_x + msg_win_w > panel->x + panel->w) + { + msg_win_x = (panel->x + panel->w) - msg_win_w - 5; + arrow_offset = panel->x + msg->sender->x - msg_win_x + (msg->sender->w/2); + } + } + + attr.event_mask = ButtonPressMask|ButtonReleaseMask|ExposureMask; + attr.background_pixel = BlackPixel(panel->dpy, panel->screen); + attr.do_not_propagate_mask = ButtonPressMask|ButtonReleaseMask; + winmask = CWBackPixel|CWEventMask|CWDontPropagate; + + if (panel->use_overide_wins) + { + winmask |= CWOverrideRedirect; + attr.override_redirect = True; + } + + panel->msg_win = XCreateWindow(panel->dpy, panel->win_root, + msg_win_x, + msg_win_y, + msg_win_w, + msg_win_h, 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + winmask, + &attr); + + XChangeProperty(panel->dpy, panel->msg_win, + panel->atoms[ATOM_WM_WINDOW_TYPE], + XA_ATOM, 32, PropModeReplace, + (unsigned char *) + &panel->atoms[ATOM_WM_WINDOW_TYPE_SPLASH], 1); + + wm_hints = XAllocWMHints(); + wm_hints->input = False; + wm_hints->flags = InputHint; + XSetWMHints(panel->dpy, panel->msg_win, wm_hints); + + tmp_drw = mb_drawable_new(panel->pb, msg_win_w, msg_win_h); + + img_backing = mb_pixbuf_img_rgba_new(panel->pb, msg_win_w, msg_win_h); + + mask = XCreatePixmap(panel->dpy, panel->win_root, msg_win_w, msg_win_h, 1 ); + mask_gc = XCreateGC(panel->dpy, mask, 0, 0 ); + + XSetForeground(panel->dpy, mask_gc, WhitePixel( panel->dpy, panel->screen )); + XFillRectangle(panel->dpy, mask, mask_gc, 0, 0, msg_win_w, msg_win_h ); + XSetForeground(panel->dpy, mask_gc, BlackPixel( panel->dpy, panel->screen )); + + /* + * - fill entire block with forground color + * - alpha clear part + * - draw arrow part + * - curve corners ? + */ + + r = mb_col_red(panel->msg_col); + g = mb_col_green(panel->msg_col); + b = mb_col_blue(panel->msg_col); + + fr = mb_col_red(panel->msg_fg_col); + fg = mb_col_green(panel->msg_fg_col); + fb = mb_col_blue(panel->msg_fg_col); + + mb_pixbuf_img_fill (panel->pb, img_backing, r, g, b, 255); + + /* border */ + + /* top/bottom */ + for (x = box_x_offset; x < box_x_offset + box_w; x++) + { + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, box_y_offset, + fr, fg, fb); + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, + box_h + box_y_offset - 1, + fr, fg, fb); + } + + /* sides */ + for (y = box_y_offset; y < box_y_offset + box_h; y++) + { + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, box_x_offset, y, + fr, fg, fb); + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, + box_w + box_x_offset - 1, y, + fr, fg, fb); + } + + /* arrow */ + + switch (panel->orientation) + { + case East: + + for (x= box_w; x < msg_win_w; x++) + for (y=0; y < box_h; y++) + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + + XSetForeground( panel->dpy, mask_gc, + WhitePixel( panel->dpy, panel->screen )); + + for (x= msg_win_w - 1; x > msg_win_w - ARROW_SIZE - 2 ; x--) + { + for (y = arrow_offset - cnt; y <= arrow_offset + cnt; y++) + { + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + if ( y == (arrow_offset - cnt) || y == (arrow_offset + cnt) ) + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, + fr, fg, fb); + else + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, r,g,b); + } + cnt++; + } + + break; + + case West: + + for (x=0; x<ARROW_SIZE; x++) + for (y=0; y < msg_win_h; y++) + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + + XSetForeground( panel->dpy, mask_gc, + WhitePixel( panel->dpy, panel->screen )); + + for (x=0; x<ARROW_SIZE + 1; x++) + { + for (y = arrow_offset - cnt; y <= arrow_offset + cnt; y++) + { + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + if ( y == (arrow_offset - cnt) || y == (arrow_offset + cnt) ) + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, + fr, fg, fb); + else + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, r,g,b); + } + cnt++; + } + + break; + + case South: + + for (y=msg_win_h-ARROW_SIZE; y < msg_win_h; y++) + for (x=0; x < msg_win_w; x++) + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + + XSetForeground( panel->dpy, mask_gc, + WhitePixel( panel->dpy, panel->screen )); + + for (y=msg_win_h; y >= msg_win_h - ARROW_SIZE - 1; y--) + { + for (x = arrow_offset - cnt; x <= arrow_offset + cnt; x++) + { + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + if ( x == (arrow_offset - cnt) || x == (arrow_offset + cnt) ) + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, + fr, fg, fb); + else + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, r,g,b); + } + cnt++; + } + + break; + + case North: + + for (y=0; y < ARROW_SIZE; y++) + for (x=0; x < msg_win_w; x++) + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + + XSetForeground( panel->dpy, mask_gc, + WhitePixel( panel->dpy, panel->screen )); + + for (y=0; y < ARROW_SIZE + 1; y++) + { + for (x = arrow_offset - cnt; x <= arrow_offset + cnt; x++) + { + XDrawPoint(panel->dpy, mask, mask_gc, x, y); + + if ( x == (arrow_offset - cnt) || x == (arrow_offset + cnt) ) + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, + fr, fg, fb); + else + mb_pixbuf_img_plot_pixel(panel->pb, img_backing, x, y, r,g,b); + } + + cnt++; + } + + break; + + } + + /* Render picture to pixmap */ + mb_pixbuf_img_render_to_drawable(panel->pb, img_backing, + mb_drawable_pixmap(tmp_drw), + 0, 0); + + + mb_font_set_color(panel->msg_font, panel->msg_fg_col); + + txt_v_offset = MSG_WIN_Y_PAD + box_y_offset; + + /* + i = strlen(msg->sender->name); + while (_get_text_length(panel, msg->sender->name, i) > ( box_w + ( 2 * MSG_TEXT_MARGIN )) + && i > 0) + i--; + */ + + /* Title */ + + XSetForeground(panel->dpy, panel->msg_gc, mb_col_xpixel(panel->msg_fg_col)); + + mb_font_render_simple (panel->msg_font, + tmp_drw, + MSG_TEXT_MARGIN + box_x_offset, + txt_v_offset, + box_w, /* XXX - TEXT_MARGIN ? */ + (unsigned char *)msg->sender->name, + MB_ENCODING_UTF8, + 0); + + /* close box */ + + XDrawRectangle(panel->dpy, mb_drawable_pixmap(tmp_drw), panel->msg_gc, + box_x_offset + box_w - MSG_TEXT_MARGIN - mb_font_get_height(panel->msg_font), + txt_v_offset, + mb_font_get_height(panel->msg_font), + mb_font_get_height(panel->msg_font) ); + + XDrawLine(panel->dpy, mb_drawable_pixmap(tmp_drw), panel->msg_gc, + box_x_offset + box_w - MSG_TEXT_MARGIN - mb_font_get_height(panel->msg_font), + txt_v_offset , + box_x_offset + box_w - MSG_TEXT_MARGIN, + txt_v_offset + mb_font_get_height(panel->msg_font)); + + XDrawLine(panel->dpy, mb_drawable_pixmap(tmp_drw), panel->msg_gc, + box_x_offset + box_w - MSG_TEXT_MARGIN - mb_font_get_height(panel->msg_font), + txt_v_offset + mb_font_get_height(panel->msg_font), + box_x_offset + box_w - MSG_TEXT_MARGIN, + txt_v_offset ); + + /* Title underline */ + + XDrawLine(panel->dpy, mb_drawable_pixmap(tmp_drw), panel->msg_gc, + box_x_offset + MSG_TEXT_MARGIN / 2 , + txt_v_offset + mb_font_get_height(panel->msg_font) + ( MSG_LINE_SPC / 2 ), + box_x_offset + box_w - (MSG_TEXT_MARGIN / 2), + txt_v_offset + mb_font_get_height(panel->msg_font) + ( MSG_LINE_SPC / 2 ) ); + + /* Forward render postion on */ + txt_v_offset += mb_font_get_height(panel->msg_font) + MSG_LINE_SPC; + + /* render msg text */ + mb_layout_render (layout, tmp_drw, MSG_TEXT_MARGIN + box_x_offset, txt_v_offset, 0); + + txt_v_offset += mb_layout_height(layout); + + /* Context button, if applicable */ + if (msg->extra_context_data) + { + int context_width = mb_font_get_txt_width ( panel->msg_font, + msg->extra_context_data, + strlen(msg->extra_context_data), + MB_ENCODING_UTF8); + + XSetForeground( panel->dpy, panel->msg_gc, mb_col_xpixel(panel->msg_link_col)); + + panel->msg_context_y1 = txt_v_offset; + panel->msg_context_y2 = txt_v_offset + mb_font_get_height(panel->msg_font); + + mb_font_set_color(panel->msg_font, panel->msg_link_col); + + mb_font_render_simple (panel->msg_font, + tmp_drw, + MSG_TEXT_MARGIN + box_x_offset, + txt_v_offset, + box_w, + (unsigned char *)msg->extra_context_data, + MB_ENCODING_UTF8, + 0); + + /* underline */ + + XDrawLine(panel->dpy, mb_drawable_pixmap(tmp_drw), panel->msg_gc, + MSG_TEXT_MARGIN + box_x_offset, + txt_v_offset + mb_font_get_height(panel->msg_font), + MSG_TEXT_MARGIN + box_x_offset + context_width, + txt_v_offset + mb_font_get_height(panel->msg_font)); + } + + + XSetWindowBackgroundPixmap(panel->dpy, panel->msg_win, + mb_drawable_pixmap(tmp_drw)); + + + mb_drawable_unref(tmp_drw); + + XShapeCombineMask( panel->dpy, panel->msg_win, ShapeBounding, 0, 0, mask, ShapeSet); + + XMapWindow(panel->dpy, panel->msg_win); + XFree(wm_hints); + + mb_pixbuf_img_free(panel->pb, img_backing); + XFreePixmap(panel->dpy, mask); +} + + +void +msg_handle_events(MBPanel *panel, XEvent *e) +{ + MBPanelMessageQueue *msg = NULL; + + for(msg=panel->msg_queue_start; msg != NULL; msg=msg->next) + { + if (msg->pending) + { + if (panel->msg_win) + { + XDestroyWindow(panel->dpy, panel->msg_win); + panel->msg_win = None; + } + + panel->msg_starttime = msg->starttime; + panel->msg_timeout = msg->timeout; + panel->msg_win_sender = msg->sender; + + panel->msg_has_context = False; + + if ((msg->extra_context_data = util_get_utf8_prop(panel, panel->msg_win_sender->win, panel->atoms[ATOM_MB_SYSTEM_TRAY_CONTEXT])) != NULL) + panel->msg_has_context = True; + + msg_win_create(panel, msg); + + msg_destroy(panel, msg); + + return; + } + } + + if (panel->msg_win) + { + switch (e->type) + { + case Expose: + break; + case ButtonRelease: + if ( e->xbutton.window == panel->msg_win) + { + if (panel->msg_has_context + && e->xbutton.y >= panel->msg_context_y1 + && e->xbutton.y <= panel->msg_context_y2) + { + _send_tray_context_message(panel, panel->msg_win_sender->win); + } + + XDestroyWindow(panel->dpy, panel->msg_win); + panel->msg_win = None; + } + break; + } + } +} + +void +msg_handle_timeouts(MBPanel *d) +{ + if (d->msg_win) + { + if (d->msg_timeout) /* NON ZERO */ + { + if ((d->msg_starttime+d->msg_timeout) < _get_server_time(d)) + { + XDestroyWindow(d->dpy, d->msg_win); + d->msg_win = None; + return; + } + } + } +} + +static MBLayout* +msg_calc_win_size(MBPanel *panel, MBPanelMessageQueue *m, int *w, int *h) +{ + MBLayout *layout; + + int title_width = 0, context_width = 0; + + *w = 0; + *h = 0; + + layout = mb_layout_new (); + + mb_layout_set_font(layout, panel->msg_font); + mb_layout_set_text(layout, m->data, MB_ENCODING_UTF8); + + mb_layout_get_geometry(layout, w, h); + + /* title + line */ + *h += (mb_font_get_height(panel->msg_font) + MSG_LINE_SPC) ; + + title_width = mb_font_get_txt_width(panel->msg_font, + m->sender->name, strlen(m->sender->name), + MB_ENCODING_UTF8) + + (2 * mb_font_get_height(panel->msg_font)); + + if (title_width > *w) *w = title_width; + + if (m->extra_context_data) + { + context_width = mb_font_get_txt_width(panel->msg_font, + m->extra_context_data, + strlen(m->extra_context_data), + MB_ENCODING_UTF8); + + if (context_width > *w) *w = context_width; + + *h += mb_font_get_height(panel->msg_font); + } + + *w += ( 2 * MSG_TEXT_MARGIN ); + + return layout; +} + + diff --git a/src/msg.h b/src/msg.h new file mode 100644 index 0000000..5fe90ef --- /dev/null +++ b/src/msg.h @@ -0,0 +1,20 @@ +#ifndef _MSG_H_ +#define _MSG_H_ + +#include <string.h> + +#include "panel.h" +#include "panel_app.h" + +#define MSG_WIN_Y_PAD 5 +#define MSG_BUB_POLY 15 + +#define MSG_LINE_SPC 8 + +MBPanelMessageQueue* msg_new(MBPanel *dock, XClientMessageEvent *e); +void msg_destroy(MBPanel *d, MBPanelMessageQueue *m); +void msg_add_data(MBPanel *d, XClientMessageEvent *e); +void msg_handle_events(MBPanel *d, XEvent *e); +void msg_handle_timeouts(MBPanel *d); + +#endif diff --git a/src/panel.c b/src/panel.c new file mode 100644 index 0000000..1c4d691 --- /dev/null +++ b/src/panel.c @@ -0,0 +1,2030 @@ +/* + * mbpanel + * + * A 'system tray' - Matthew Allum <mallum@handhelds.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + +#include "panel.h" +#include "msg.h" + +MBPanel *G_panel = NULL; + +#define DEFAULT_MSG_FGCOL "black" +#define DEFAULT_MSG_BGCOL "yellow" +#define DEFAULT_MSG_BGURGCOL "orange" + +#ifdef USE_XSETTINGS + +/* + XX = system_tray_id; + +/MATCHBOX/PANEL/XX/ORIENTATION north|south|east|west +/MATCHBOX/PANEL/XX/BACKGROUND default|rgb:XXX|pxm:XXX|trans:XXX + Gtk/FontName +*/ + +#define XSET_UNKNOWN 0 +#define XSET_GTK_FONT 1 + +static void +panel_xsettings_notify_cb (const char *name, + XSettingsAction action, + XSettingsSetting *setting, + void *data) +{ + MBPanel *panel = (MBPanel *)data; + int i = 0; + int key = XSET_UNKNOWN; + + struct _mb_xsettings { char *name; int value; } mb_xsettings[] = { + { "Gtk/FontName", XSET_GTK_FONT }, + { NULL, -1 } + }; + + while( mb_xsettings[i].name != NULL ) + { + if (!strcmp(name, mb_xsettings[i].name) + && setting != NULL /* XXX set to NULL when action deleted */ + && setting->type == XSETTINGS_TYPE_STRING ) + { + key = mb_xsettings[i].value; + break; + } + i++; + } + + if (key == XSET_UNKNOWN) return; + + switch (action) + { + case XSETTINGS_ACTION_NEW: + case XSETTINGS_ACTION_CHANGED: + switch (key) + { + case XSET_GTK_FONT: + if (setting->data.v_string && strlen(setting->data.v_string)) + { + /* This will be overidden ( hopefully ) by any theme.desktop + * setting. + */ + DBG("%s() setting XSET_GTK_FONT: '%s'\n", __func__, + setting->data.v_string); + /* mb_menu_set_font (panel->mbmenu, setting->data.v_string); */ + mb_font_set_from_string(panel->msg_font, setting->data.v_string); + + } + break; + + } + case XSETTINGS_ACTION_DELETED: + /* Do nothing for now */ + break; + } +} + +#endif + + +void +panel_handle_full_panel (MBPanel *panel, MBPanelApp *bad_papp) +{ + + /* For now we just killoff the client */ + + DBG("%s() killing winbow id %li\n", __func__, bad_papp->win); + + XKillClient(panel->dpy, bad_papp->win); /* XXX should probably make this + kill more effectively */ + panel_app_destroy (panel, bad_papp); +} + +void /* XXX maybe better to add this to panel_menu_add_remove_item */ +panel_update_client_list_prop (MBPanel *panel) +{ + MBPanelApp *papp = NULL; + MBPanelApp *papp_heads[] = { panel->apps_start_head, + panel->apps_end_head, + NULL }; + int i = 0, app_cnt = 0, cnt = 0; + Window *wins = NULL; + + DBG("%s() called\n", __func__); + + /* Count number of applets docked */ + while (i < 2) + { + papp = papp_heads[i]; + while( papp != NULL) + { + if (!papp->ignore) app_cnt++; + papp = papp->next; + } + i++; + } + + if (app_cnt == 0) + { + /* delete prop ?? */ + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_NET_CLIENT_LIST] , + XA_WINDOW, 32, PropModeReplace, + (unsigned char *)NULL, 0); + return; + } + + i = 0; + wins = malloc(sizeof(Window)*app_cnt); + + while (i < 2) + { + papp = papp_heads[i]; + + if (i == 1) + papp = panel_app_list_get_last(panel, panel->apps_end_head); + + while( papp != NULL) + { + if (!papp->ignore) + { + wins[cnt++] = papp->win; + } + + if (i == 1) + { + papp = panel_app_list_get_prev (panel, papp, + &panel->apps_end_head); + } + else papp = papp->next; + } + i++; + } + + + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_NET_CLIENT_LIST], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *)wins, app_cnt); + + free(wins); + +} + + +void /* show/hiden the panel */ +panel_toggle_visibilty(MBPanel *d) +{ + +#define PANEL_HIDDEN_SIZE 6 + + + static int panel_orig_size; + MBPanelApp *papp = NULL; + MBPanelApp *papp_heads[] = { d->apps_start_head, + d->apps_end_head, + NULL }; + int i = 0; + + DBG("%s() called, x: %i, y: %i, w: %i, h: %i\n", __func__, + d->x, d->y, d->w, d->h); + + if (d->is_hidden) + { + + if (PANEL_IS_VERTICAL(d)) + { + XMoveResizeWindow(d->dpy, d->win, d->x, d->y, + panel_orig_size, d->h ); + d->w = panel_orig_size; + } else { + XMoveResizeWindow(d->dpy, d->win, d->x, d->y, d->w, + panel_orig_size); + d->h = panel_orig_size; + } + + while (i < 2) + { + papp = papp_heads[i]; + while( papp != NULL) + { + XMapWindow(d->dpy, papp->win); + panel_app_move_to (d, papp, + panel_app_get_offset (d, papp)); + papp->ignore_unmap--; + papp = papp->next; + } + i++; + } + + d->is_hidden = False; + + } else { + while (i < 2) + { + papp = papp_heads[i]; + while( papp != NULL) + { + XUnmapWindow(d->dpy, papp->win); + papp->ignore_unmap++; + papp = papp->next; + } + i++; + } + + if (PANEL_IS_VERTICAL(d)) + { + XMoveResizeWindow(d->dpy, d->win, d->x, d->y, + PANEL_HIDDEN_SIZE, d->h ); + panel_orig_size = d->w; + } else { + XMoveResizeWindow(d->dpy, d->win, d->x, d->y, + d->w, PANEL_HIDDEN_SIZE); + panel_orig_size = d->h; + } + d->is_hidden = True; + } +} + +void +panel_set_bg(MBPanel *panel, int bg_type, char *bg_spec) +{ + MBPixbufImage *img_tmp, *img_bg; + int dx, dy, dw, dh; + + Pixmap tmp_pxm; + char xprop_def[32] = { 0 }; + char *tmp_path = NULL; + + if (panel->bg_spec) free(panel->bg_spec); + panel->bg_spec = strdup(bg_spec); + panel->bg_type = bg_type; + + DBG("%s() bg_spec: %s, type %i\n", __func__, panel->bg_spec, panel->bg_type); + + XGrabServer(panel->dpy); + + switch (bg_type) + { + case BG_PIXMAP: + if (bg_spec[0] != '/' && panel->theme_path != NULL) + { + tmp_path = alloca( sizeof(char) * (strlen(panel->theme_path) + strlen(bg_spec))); + sprintf(tmp_path, "%s/%s", panel->theme_path, bg_spec); + } + else tmp_path = bg_spec; + + if ((img_tmp = mb_pixbuf_img_new_from_file(panel->pb, tmp_path)) == NULL) + { + fprintf(stderr, "mbdock: failed to load %s\n", bg_spec); + panel_set_bg(panel, BG_SOLID_COLOR, DEFAULT_COLOR_SPEC); + return; + } + + img_bg = mb_pixbuf_img_new(panel->pb, panel->w, panel->h); + + /* scale for panel height ? */ + if ( (img_tmp->height != panel->h && !PANEL_IS_VERTICAL(panel)) + || (img_tmp->width != panel->w && PANEL_IS_VERTICAL(panel)) ) + { + MBPixbufImage *img_scaled; + img_scaled = mb_pixbuf_img_scale(panel->pb, img_tmp, + img_tmp->width, + PANEL_IS_VERTICAL(panel) ? + panel->w : panel->h); + mb_pixbuf_img_free(panel->pb, img_tmp); + img_tmp = img_scaled; + } + + /* rotate */ + if (PANEL_IS_VERTICAL(panel)) + { + MBPixbufImage *img_rot; + img_rot = mb_pixbuf_img_transform (panel->pb, img_tmp, + MBPIXBUF_TRANS_ROTATE_90); + + mb_pixbuf_img_free(panel->pb, img_tmp); + img_tmp = img_rot; + } + + for (dy=0; dy < panel->h; dy += img_tmp->height) + for (dx=0; dx < panel->w; dx += img_tmp->width) + { + if ( (dx + img_tmp->width) > panel->w ) + dw = img_tmp->width - ((dx + img_tmp->width)-panel->w); + else + dw = img_tmp->width; + + if ( (dy + img_tmp->height) > panel->h ) + dh = img_tmp->height-((dy + img_tmp->height)-panel->h); + else + dh = img_tmp->height; + mb_pixbuf_img_copy(panel->pb, img_bg, img_tmp, + 0, 0, dw, dh, dx, dy); + } + + mb_pixbuf_img_free(panel->pb, img_tmp); + + if (panel->bg_pxm != None) + XFreePixmap(panel->dpy, panel->bg_pxm); + + panel->bg_pxm = XCreatePixmap(panel->dpy, panel->win_root, + panel->w, panel->h, + panel->pb->depth ); + + mb_pixbuf_img_render_to_drawable(panel->pb, img_bg, panel->bg_pxm, 0, 0); + + mb_pixbuf_img_free(panel->pb, img_bg); + + XSetWindowBackgroundPixmap(panel->dpy, panel->win, panel->bg_pxm); + XClearWindow(panel->dpy, panel->win); + + snprintf(xprop_def, 32, "pxm:%li", panel->bg_pxm); + panel->root_pixmap_id = 0; + break; + case BG_SOLID_COLOR: + if (XParseColor(panel->dpy, + DefaultColormap(panel->dpy, panel->screen), + bg_spec, + &panel->xcol )) + { + + XAllocColor(panel->dpy, + DefaultColormap(panel->dpy, panel->screen), + &panel->xcol); + + XSetWindowBackground(panel->dpy, panel->win, + panel->xcol.pixel); + XClearWindow(panel->dpy, panel->win); + + if (panel->bg_pxm != None) + XFreePixmap(panel->dpy, panel->bg_pxm); + panel->bg_pxm = None; + + snprintf(xprop_def, 32, "rgb:%li", panel->xcol.pixel); + } + panel->root_pixmap_id = 0; + break; + case BG_TRANS: + tmp_pxm = util_get_root_pixmap(panel); + if (tmp_pxm != None) + { + int trans_level = 0; + + if (bg_spec) trans_level = atoi(bg_spec); + + DBG("%s() Getting root pixmap\n", __func__); + + if (panel->bg_pxm != None) XFreePixmap(panel->dpy, panel->bg_pxm); + + panel->bg_pxm = XCreatePixmap(panel->dpy, panel->win_root, + panel->w, panel->h, + panel->pb->depth ); + + img_tmp = mb_pixbuf_img_new_from_drawable(panel->pb, + tmp_pxm, None, + panel->x, panel->y, + panel->w, panel->h); + if (img_tmp == NULL) + { + XFreePixmap(panel->dpy, panel->bg_pxm); + fprintf(stderr, "Failed to get root pixmap id\n"); + panel_set_bg(panel, BG_SOLID_COLOR, DEFAULT_COLOR_SPEC); + return; + } + + DBG("%s() First pixel of root looks like %i, %i, %i, %i\n", + __func__, img_tmp->rgba[0], img_tmp->rgba[1], + img_tmp->rgba[2], img_tmp->rgba[3]); + + if (trans_level > 0) + for (dx = 0; dx < panel->w; dx++) + for (dy = 0; dy < panel->h; dy++) + mb_pixbuf_img_plot_pixel_with_alpha(panel->pb, + img_tmp, dx, dy, + 255, 255, 255, + trans_level); + + + mb_pixbuf_img_render_to_drawable(panel->pb, img_tmp, + panel->bg_pxm, 0, 0); + + mb_pixbuf_img_free(panel->pb, img_tmp); + + + XSetWindowBackgroundPixmap(panel->dpy, panel->win, + panel->bg_pxm); + XClearWindow(panel->dpy, panel->win); + + snprintf(xprop_def, 32, "pxm:%li", panel->bg_pxm); + + } else { + fprintf(stderr, "Failed to get root pixmap id\n"); + panel->root_pixmap_id = -1; + panel_set_bg(panel, BG_SOLID_COLOR, DEFAULT_COLOR_SPEC); + return; + } + + break; + } + + if (xprop_def) + { + DBG("setting _MB_PANEL_BG to %s\n", xprop_def); + + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_MB_PANEL_BG], + XA_STRING, 8, + PropModeReplace, xprop_def, + strlen(xprop_def)); + } + + XUngrabServer(panel->dpy); + + XSync(panel->dpy, False); + +} + +Bool +panel_set_theme_from_root_prop(MBPanel *panel) +{ + Atom realType; + unsigned long n; + unsigned long extra; + int format; + int status; + char * value; + struct stat stat_info; + char panel_cfg[256]; + + DBG("%s() called\n", __func__); + + if ( panel->use_themes == False ) + { + DBG("%s() panel themeing disabled by command line options\n", __func__ ); + return False; + } + + status = XGetWindowProperty(panel->dpy, panel->win_root, + panel->atoms[ATOM_MB_THEME], + 0L, 512L, False, + AnyPropertyType, &realType, + &format, &n, &extra, + (unsigned char **) &value); + + if (status != Success || value == 0 + || *value == 0 || n == 0) + { + DBG("%s() no _MB_THEME set on root window\n", __func__ ); + return False; + } else { + + int i = 0; + MBPanelApp *papp = NULL; + MBPanelApp *papp_heads[] = { panel->apps_start_head, + panel->apps_end_head, + NULL }; + + strcpy(panel_cfg, value); + strcat(panel_cfg, "/theme.desktop"); + + if (stat(panel_cfg, &stat_info) != -1) + { + MBDotDesktop *theme = NULL; + theme = mb_dotdesktop_new_from_file(panel_cfg); + if (theme) + { + if (panel->theme_path) free(panel->theme_path); + panel->theme_path = strdup(value); + + /* Different theme values for panel in titlebar */ + if (panel->want_titlebar_dest) + { + if (mb_dotdesktop_get(theme, "TitlebarDockBgColor")) + { + panel_set_bg(panel, BG_SOLID_COLOR, + mb_dotdesktop_get(theme, "TitlebarDockBgColor")); + } + + if (mb_dotdesktop_get(theme, "TitlebarDockBgPixmap")) + { + panel_set_bg(panel, BG_PIXMAP, + mb_dotdesktop_get(theme, "TitlebarDockBgPixmap")); + } + + /* Newer settings */ + + if (mb_dotdesktop_get(theme, "TitlebarPanelBgColor")) + { + panel_set_bg(panel, BG_SOLID_COLOR, + mb_dotdesktop_get(theme, "TitlebarPanelBgColor")); + } + + if (mb_dotdesktop_get(theme, "TitlebarPanelBgPixmap")) + { + panel_set_bg(panel, BG_PIXMAP, + mb_dotdesktop_get(theme, "TitlebarPanelBgPixmap")); + } + + + } + else + { + + /* + * FIXME: Need to phase out the Dock prefix to panel + */ + + if (mb_dotdesktop_get(theme, "DockBgColor")) + { + panel_set_bg(panel, BG_SOLID_COLOR, + mb_dotdesktop_get(theme, "DockBgColor")); + } + if (mb_dotdesktop_get(theme, "DockBgTrans")) + { + panel_set_bg(panel, BG_TRANS, + mb_dotdesktop_get(theme, "DockBgTrans")); + } + if (mb_dotdesktop_get(theme, "DockBgPixmap")) + { + panel_set_bg(panel, BG_PIXMAP, + mb_dotdesktop_get(theme, "DockBgPixmap")); + } + + /* Newer setting below */ + + if (mb_dotdesktop_get(theme, "PanelBgColor")) + { + panel_set_bg(panel, BG_SOLID_COLOR, + mb_dotdesktop_get(theme, "PanelBgColor")); + } + if (mb_dotdesktop_get(theme, "PanelBgTrans")) + { + panel_set_bg(panel, BG_TRANS, + mb_dotdesktop_get(theme, "PanelBgTrans")); + } + if (mb_dotdesktop_get(theme, "PanelBgPixmap")) + { + panel_set_bg(panel, BG_PIXMAP, + mb_dotdesktop_get(theme, "PanelBgPixmap")); + } + + + } + + if (mb_dotdesktop_get(theme, "PanelMsgBgCol")) + util_xcol_from_spec(panel, panel->msg_col, + mb_dotdesktop_get(theme, "PanelMsgBgCol")); + else + util_xcol_from_spec(panel, panel->msg_col, + DEFAULT_MSG_BGCOL); + + + + + if (mb_dotdesktop_get(theme, "PanelMsgBgUrgentCol")) + util_xcol_from_spec(panel, panel->msg_urgent_col, + mb_dotdesktop_get(theme, + "PanelMsgBgUrgentCol")); + else + util_xcol_from_spec(panel, panel->msg_urgent_col, + DEFAULT_MSG_BGURGCOL); + + if (mb_dotdesktop_get(theme, "PanelMsgFgCol")) + util_xcol_from_spec(panel, panel->msg_fg_col, + mb_dotdesktop_get(theme, + "PanelMsgFgCol")); + else + util_xcol_from_spec(panel, panel->msg_fg_col, + DEFAULT_MSG_FGCOL); + mb_dotdesktop_free(theme); + + } + } + + if (value) XFree(value); + + status = XGetWindowProperty(panel->dpy, panel->win_root, + panel->atoms[ATOM_MB_THEME_NAME], + 0L, 512L, False, + AnyPropertyType, &realType, + &format, &n, &extra, + (unsigned char **) &value); + + if (status && value) + { + if (panel->theme_name) free(panel->theme_name); + panel->theme_name = strdup(value); + } + + if (value) XFree(value); + + /* Now retheme the panel menu */ + panel_menu_init(panel); + + while (i < 2) + { + papp = papp_heads[i]; + while( papp != NULL ) + { + panel_menu_update_remove_items(panel); + papp = papp->next; + } + i++; + } + + return True; + } + return False; +} + +void +panel_usage(char *bin_name) +{ + fprintf(stderr, "%s usage: %s [Options...]\n" + "Where options are;\n" + "-display, -d <X11 Display name>\n" + "-geometry, -g Use --size / --orientation instead.\n" + "--id <int> Panel ID\n" + "--size, -s <int> width/height of dock in pixels\n" + "--orientation <north|east|south|west>\n" + "--default-apps, -da <app list> comma seperated list of apps to\n" + " add to a tray when no session exists\n" + "--margin-start <+int> initial app offset in pixels (panel start)\n" + "--margin-end <+int> initial app offset in pixels (panel end)\n" + "--titlebar Request panel in titlebar - see docs for limitations\n" + "--no-session, -ns No session saving.\n" + "--no-menu, -nm No popup menu\n" + "--no-flip, -nf On a display rotation, stop the panel from\n" + " From rotating itself too\n" + "--overide-bubbles, -o\n\n" + "Background options:\n" + "--bgcolor, -c <color spec>\n" + "--bgpixmap, -b <image file>\n" + "--bgtrans, -bt <'yes'|transparency percentage>\n" + "*NOTE* setting the background here will disable the effect\n" + " of any external theme changes. \n" + , bin_name, bin_name); + exit(1); +} + +/* XEMBED */ + +int +panel_get_map_state(MBPanel *d, MBPanelApp *c) +{ + Atom realType; + unsigned long n; + unsigned long extra; + int format; + int status; + CARD32 * value = NULL; + int result = -1; + + status = XGetWindowProperty(d->dpy, c->win, + d->atoms[ATOM_XEMBED_INFO], + 0L, 10L, + 0, XA_ATOM, &realType, &format, + &n, &extra, (unsigned char **) &value); + if (status == Success) + { + if (realType == XA_CARDINAL && format == 32) + { + /* + printf("VERSION: %i\n", value[0]); + printf("MAPPED: %i\n", value[1]); + */ + + result = (int)value[1]; + + } + } + + if (value) XFree(value); + + return result; +} + +void panel_send_xembed_message( + MBPanel *d, + MBPanelApp *c, + long message, /* message opcode */ + long detail, /* message detail */ + long data1, /* message data 1 */ + long data2 /* message data 2 */ + ){ + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = d->atoms[ATOM_XEMBED_MESSAGE]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = message; + ev.xclient.data.l[2] = detail; + ev.xclient.data.l[3] = data1; + ev.xclient.data.l[4] = data2; + XSendEvent(d->dpy, c->win, False, NoEventMask, &ev); + XSync(d->dpy, False); +} + +void panel_send_manage_message( MBPanel *d ) +{ + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = d->win_root; + ev.xclient.message_type = d->atoms[ATOM_MANAGER]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = d->atoms[ATOM_SYSTEM_TRAY]; + ev.xclient.data.l[2] = d->win; + + XSendEvent(d->dpy, d->win_root, False, StructureNotifyMask, &ev); + XSync(d->dpy, False); +} + + + +/* Events */ + +void +panel_handle_button_event(MBPanel *panel, XButtonEvent *e) +{ + XEvent ev; + int done = 0; + struct timeval then, now; + + DBG("%s() called, subwindow : %li\n", __func__, e->subwindow ); + + if (!panel->use_menu) return; /* menu disabled */ + + if (e->window != panel->win) return; + + if (panel->is_hidden) + { + panel_toggle_visibilty(panel); + return; + } + + gettimeofday(&then, NULL); + while (!done) { + if (XCheckMaskEvent(panel->dpy,ButtonReleaseMask, &ev)) + if (ev.type == ButtonRelease) done=1; + gettimeofday(&now, NULL); + if ( (now.tv_usec-then.tv_usec) > (panel->click_time*1000) ) + done=2; + } + + if (done == 2 && !mb_menu_is_active(panel->mbmenu)) + { + int dpy_h = DisplayHeight(panel->dpy, panel->screen); + util_get_mouse_position(panel, &panel->click_x, &panel->click_y); + mb_menu_activate(panel->mbmenu, + (panel->click_x-5 < 0) ? 2 : panel->click_x-5, + (panel->click_y+5 > dpy_h) ? dpy_h-2 : panel->click_y+5); + } +} + +void +panel_handle_expose(MBPanel *panel, XExposeEvent *e) +{ + ; +} + +void +panel_handle_dock_request(MBPanel *panel, Window win) +{ + int app_origin_dist = 0; + char *cmd_str = NULL; + MBPanelApp *new_papp = NULL; + + util_get_command_str_from_win(panel, win, &cmd_str); /* cmd_str freed l8r */ + + if (session_preexisting_win_matches_wanted(panel, win, cmd_str)) + { + app_origin_dist = panel->session_init_offset; + session_preexisting_clear_current(panel); + } + + new_papp = panel_app_new(panel, win, cmd_str); + + if (new_papp) + { + XSelectInput(panel->dpy, new_papp->win, PropertyChangeMask ); + + /* tell app its docked - panel app should now map its self */ + panel_send_xembed_message(panel, new_papp, + XEMBED_EMBEDDED_NOTIFY, + 0, panel->win, 0); + + /* sent when it gets focus */ + panel_send_xembed_message(panel, new_papp, + XEMBED_WINDOW_ACTIVATE, + 0,0,0); + + XMapWindow(panel->dpy, new_papp->win); + new_papp->mapped = True; + + panel_menu_update_remove_items(panel); + } + + session_save(panel); + + session_preexisting_start_next(panel); +} + +void +panel_handle_client_message(MBPanel *panel, XClientMessageEvent *e) +{ + DBG("%s() called\n", __func__ ); + if (e->message_type + == panel->atoms[ATOM_SYSTEM_TRAY_OPCODE]) + { + DBG("%s() got system tray message\n", __func__ ); + switch (e->data.l[1]) + { + case SYSTEM_TRAY_REQUEST_DOCK: + DBG("%s() is SYSTEM_TRAY_REQUEST_DOCK\n", __func__ ); + panel_handle_dock_request(panel, e->data.l[2]); + break; + case SYSTEM_TRAY_BEGIN_MESSAGE: + DBG("%s() is SYSTEM_TRAY_BEGIN_MESSAGE\n", __func__ ); + msg_new(panel, e); + break; + case SYSTEM_TRAY_CANCEL_MESSAGE: + DBG("%s() is SYSTEM_TRAY_CANCEL_MESSAGE\n", __func__ ); + break; + } + return; + } + + if (e->message_type + == panel->atoms[ATOM_MB_COMMAND]) + { + switch (e->data.l[0]) + { + case MB_CMD_PANEL_TOGGLE_VISIBILITY: + panel_toggle_visibilty(panel); + break; + case MB_CMD_PANEL_SIZE: + case MB_CMD_PANEL_ORIENTATION: + default: + break; + } + + } + + if (e->message_type + == panel->atoms[ATOM_NET_SYSTEM_TRAY_MESSAGE_DATA]) + { + DBG("%s() got system tray message _data_\n", __func__ ); + msg_add_data(panel, e); + return; + } + + if (e->message_type + == panel->atoms[ATOM_WM_DELETE_WINDOW]) + { + util_cleanup_children(0); + } + + return; +} + +void +panel_handle_property_notify(MBPanel *panel, XPropertyEvent *e) +{ + MBPanelApp *papp; + + if (e->atom == panel->atoms[ATOM_XEMBED_INFO]) + { + int i; + papp = panel_app_get_from_window(panel, e->window); + DBG("%s() got XEMBED_INFO property notify for %s\n", + __func__, papp->name ); + if (papp != NULL) + { + i = panel_get_map_state(panel, papp); + if (i == 1) + { + XMapRaised(panel->dpy, papp->win); + papp->mapped = True; + } + if (i == 0) + { + XUnmapWindow(panel->dpy, papp->win); + papp->mapped = False; + } + } + return; + } + + if (e->atom == panel->atoms[ATOM_MB_THEME]) + { + panel_set_theme_from_root_prop(panel); + return; + } + + if (e->atom == panel->atoms[ATOM_XROOTPMAP_ID] + && panel->root_pixmap_id != 0) + { + panel_set_bg(panel, BG_TRANS, panel->bg_trans); + return; + } + + if (e->atom == panel->atoms[ATOM_MB_REQ_CLIENT_ORDER]) + { + panel_reorder_apps(panel); + } +} + +static Bool +get_xevent_timed(Display* dpy, XEvent* event_return, struct timeval *tv) +{ + if (tv == NULL) + { + XNextEvent(dpy, event_return); + return True; + } + + XFlush(dpy); + + if (XPending(dpy) == 0) + { + int fd = ConnectionNumber(dpy); + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + if (select(fd+1, &readset, NULL, NULL, tv) == 0) + { + return False; + } else { + XNextEvent(dpy, event_return); + return True; + } + } else { + XNextEvent(dpy, event_return); + return True; + } +} + +void +panel_main(MBPanel *panel) +{ + MBPanelApp *papp; + XEvent an_event; + int xfd; + Bool had_rotation = False; + + XSelectInput (panel->dpy, panel->win, StructureNotifyMask|ExposureMask| + SubstructureRedirectMask|SubstructureNotifyMask| + ButtonPressMask|ButtonReleaseMask|PointerMotionMask| + PropertyChangeMask); + + XSelectInput (panel->dpy, panel->win_root, + PropertyChangeMask|StructureNotifyMask); + + xfd = ConnectionNumber (panel->dpy); + + XFlush(panel->dpy); + + while(1) + { + struct timeval tvt; + tvt.tv_usec = 500; + tvt.tv_sec = 0; + + if (get_xevent_timed(panel->dpy, &an_event, &tvt)) + { +#ifdef USE_XSETTINGS + if (panel->xsettings_client != NULL) + xsettings_client_process_event(panel->xsettings_client, &an_event); +#endif + mb_menu_handle_xevent(panel->mbmenu, &an_event); + switch (an_event.type) + { + case ButtonPress: + panel_handle_button_event(panel, &an_event.xbutton); + break; + case ClientMessage: + panel_handle_client_message(panel, &an_event.xclient); + break; + case PropertyNotify: + panel_handle_property_notify(panel, &an_event.xproperty); + break; + case MapRequest: + break; + case UnmapNotify: + /* window should unmap ... we destroy it */ + papp = panel_app_get_from_window(panel, + an_event.xunmap.window); + if (papp && !papp->ignore_unmap) + { + panel_app_destroy(panel, papp); + + /* remove any connected message windows */ + if (panel->msg_win && panel->msg_win_sender == papp) + { + XDestroyWindow(panel->dpy, panel->msg_win); + panel->msg_win = None; + } + + /* + * We set an alarm here, so the session file is only + * updated after a couple fo seconds. + * This is done so if the xserver is closing and bringing + * down all the clients. Its very likely it'll kill + * a panelapp before the panel, and we dont really want it + * to be refmoved from the session file. + */ + alarm(3); + } + break; + case Expose: + panel_handle_expose(panel, &an_event.xexpose); + break; + case DestroyNotify: + break; + case ConfigureRequest: + panel_app_handle_configure_request(panel, + &an_event.xconfigurerequest); + break; + case ConfigureNotify: + if (an_event.xconfigure.window == panel->win_root) + { + had_rotation = True; + DBG("%s() **** HAD ROTATION ***\n", __func__); + break; + } + + if (an_event.xconfigure.window == panel->win) + { + DBG("%s(): configureNotify on panel\n", __func__); + /* These can be confused by a flip */ + if (an_event.xconfigure.send_event) + break; + + if (panel->w != an_event.xconfigure.width + || panel->h != an_event.xconfigure.height) + { + int diff = 0; + MBPanelApp *papp = NULL; + + if (panel->ignore_next_config) + { + panel->ignore_next_config = False; + break; + } + + if (panel->use_flip && had_rotation) + { + /* Flip if are length is changed + XXX a little hacky XXXX + */ + int dpy_w, dpy_h; + XWindowAttributes root_attr; + + XGetWindowAttributes(panel->dpy, + panel->win_root, + &root_attr); + + dpy_w = root_attr.width; + dpy_h = root_attr.height; + + had_rotation = False; + + if ((PANEL_IS_VERTICAL(panel) + && (an_event.xconfigure.width == panel->w) + ) + || + (!PANEL_IS_VERTICAL(panel) + && (an_event.xconfigure.height == panel->h) + /* && (an_event.xconfigure.width == dpy_w) + && dpy_w != panel->w */ ) + ) + { + + DBG("%s() flipping ....\n", __func__); + + panel->ignore_next_config = True; + + switch (panel->orientation) + { + case South: + panel_change_orientation(panel, East, + dpy_w, dpy_h); + break; + case North: + panel_change_orientation(panel, West, + dpy_w, dpy_h); + break; + case West: + panel_change_orientation(panel, North, + dpy_w, dpy_h); + break; + case East: + panel_change_orientation(panel, South, + dpy_w, dpy_h); + break; + } + break; + } + } + + if (PANEL_IS_VERTICAL(panel)) + { + diff = an_event.xconfigure.height - panel->h; + if (an_event.xconfigure.y > panel->y) + papp = panel->apps_start_head; + else + papp = panel->apps_end_head; + } else { + diff = an_event.xconfigure.width - panel->w; + if (an_event.xconfigure.x > panel->x) + papp = panel->apps_start_head; + else + papp = panel->apps_end_head; + } + + panel->w = an_event.xconfigure.width; + panel->h = an_event.xconfigure.height; + + panel_apps_nudge (panel, papp, diff); + + /* Nake sure bg gets updated */ + if (!(panel->use_flip && had_rotation)) + { + char *tmp_str = NULL; + if (panel->bg_spec) + tmp_str = strdup(panel->bg_spec) ; + panel_set_bg(panel, panel->bg_type, tmp_str); + if (tmp_str) free(tmp_str); + } + + // panel_apps_rescale (panel, panel->apps_start_head); + // panel_apps_rescale (panel, panel->apps_end_head); + } + + panel->x = an_event.xconfigure.x; + panel->y = an_event.xconfigure.y; + + DBG("%s() config notify, got x: %i , y: %i w: %i h: %i \n", + __func__, + an_event.xconfigure.x, + an_event.xconfigure.y, + an_event.xconfigure.width, + an_event.xconfigure.height); + + DBG("%s() panel is now x: %i , y: %i w: %i h: %i \n", + __func__, panel->x, panel->y, + panel->w, panel->h ); + } + break; + } + msg_handle_events(panel, &an_event); + } + session_preexisting_handle_timeouts(panel); + msg_handle_timeouts(panel); + } +} + +static void +panel_orientation_set_hint (MBPanel *panel) +{ + int is_vertical[1] = { 0 }; + + if (PANEL_IS_VERTICAL(panel)) is_vertical[0] = 1; + + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_NET_SYSTEM_TRAY_ORIENTATION], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)is_vertical , 1); +} + + +void +panel_change_orientation(MBPanel *panel, + MBPanelOrientation new_orientation, + int dpy_w, int dpy_h) +{ + char *tmp_str = NULL; + + XUnmapWindow(panel->dpy, panel->win); + + if ( ( (panel->orientation == East || panel->orientation == West) + && (new_orientation == North || new_orientation == South) ) + || + ( (panel->orientation == North || panel->orientation == South) + && (new_orientation == East || new_orientation == West ) ) + ) + { + MBPanelApp *papp_heads[] = { panel->apps_start_head, + panel->apps_end_head, + NULL }; + MBPanelApp *papp_cur = NULL; + int i = 0; + + panel->x = 0; + panel->y = 0; + panel->h = panel->default_panel_size; + panel->w = dpy_w; + + switch (new_orientation) + { + case South: + panel->y = dpy_h - panel->h; + break; + case North: + break; + case West: + panel->w = panel->default_panel_size; + panel->h = dpy_h; + break; + case East: + panel->w = panel->default_panel_size; + panel->h = dpy_h; + panel->x = dpy_w - panel->w; + break; + } + + panel->orientation = new_orientation; + + panel_orientation_set_hint(panel); + + DBG("%s() setting panel x: %i , y: %i w: %i h: %i \n", + __func__, panel->x, panel->y, + panel->w, panel->h ); + + XMoveResizeWindow( panel->dpy, panel->win, panel->x, panel->y, + panel->w, panel->h ); + + /* move_to() will reposition each app */ + while (i < 2) + { + papp_cur = papp_heads[i]; + DBG("%s() moveing to, cur is i: %i ( %p )\n", + __func__, i, papp_cur); + + while (papp_cur != NULL) + { + int tmp; + tmp = papp_cur->w; + papp_cur->w = papp_cur->h; + papp_cur->h = tmp; + + if (panel->orientation == North || panel->orientation == South) + { + papp_cur->x = papp_cur->offset; + papp_cur->y = (panel->h - papp_cur->h) / 2; + } + else + { + papp_cur->y = papp_cur->offset; + papp_cur->x = (panel->w - papp_cur->w) / 2; + } + + XMoveWindow(panel->dpy, papp_cur->win, + papp_cur->x, papp_cur->y); + + panel_app_deliver_config_event(panel, papp_cur); + papp_cur = papp_cur->next; + } + i++; + } + } + else + { + switch (new_orientation) + { + case South: + panel->y = dpy_h - panel->h; + break; + case North: + panel->y = 0; + break; + case West: + panel->x = 0; + break; + case East: + panel->x = dpy_w - panel->w; + break; + } + + panel->orientation = new_orientation; + + panel_orientation_set_hint(panel); + + XMoveResizeWindow( panel->dpy, panel->win, panel->x, panel->y, + panel->w, panel->h ); + } + + if (panel->bg_spec) tmp_str = strdup(panel->bg_spec) ; + panel_set_bg(panel, panel->bg_type, tmp_str); + if (tmp_str) free(tmp_str); + + XMapWindow(panel->dpy, panel->win); + XMapSubwindows(panel->dpy, panel->win); + +} + + + +void +panel_reorder_apps(MBPanel *panel) +{ + MBPanelApp *papp, *papp_tmp; + int i, watermark = 0, offset = 0; + + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after = 0; + unsigned char *prop = NULL; + int n_wins; + Window *wins; + + if (XGetWindowProperty (panel->dpy, panel->win, + panel->atoms[ATOM_MB_REQ_CLIENT_ORDER], + 0, 1000L, False, XA_WINDOW, + &actual_type, &actual_format, + &nitems, &bytes_after, &prop) != Success) + return; + + wins = (Window *)prop; + n_wins = (int)nitems; + + papp = panel->apps_start_head; + + while( papp != NULL) + { + watermark++; + papp = papp->next; + } + + DBG("%s() watermark is %i\n", __func__, watermark); + + for (i =0; i<n_wins; i++) + { + papp = panel_app_get_from_window(panel, wins[i]); + + if (papp) + { + MBPanelApp *found_head = NULL; + /* find where it currently is */ + papp_tmp = panel->apps_start_head; + + DBG("%s() %i looping for %s\n", __func__, i, papp->name); + + while( papp_tmp != NULL) + { + if (papp == papp_tmp) + { + found_head = panel->apps_start_head; + break; + } + + papp_tmp = papp_tmp->next; + } + + if (!found_head) + { + papp_tmp = panel->apps_end_head; + + while( papp_tmp != NULL) + { + if (papp == papp_tmp) + { + found_head = panel->apps_end_head; + break; + } + + papp_tmp = papp_tmp->next; + } + } + + if (found_head) + { + if (found_head == panel->apps_start_head) + { + DBG("%s() %i found head is start\n", __func__, i ); + panel_app_list_remove (panel, papp, &panel->apps_start_head); + } + else + { + DBG("%s() %i found head is end\n", __func__, i ); + panel_app_list_remove (panel, papp, &panel->apps_end_head); + + } + + papp->next = NULL; + + if (!watermark || (watermark && i >= watermark)) + { + DBG("%s() %i prepending at end\n", __func__, i ); + + /* + if (found_head != panel->apps_end_head) switched lists + panel_app_list_append(panel, &panel->apps_end_head, papp); + else + */ + panel->apps_end_head = panel_app_list_prepend(panel, panel->apps_end_head, papp); + papp->gravity = PAPP_GRAVITY_END; + //panel_app_add_end(panel, papp); + } + else + { + DBG("%s() %i appending at start\n", __func__, i ); + panel_app_list_append(panel, &panel->apps_start_head, papp); + //panel_app_add_start(panel, papp); + papp->gravity = PAPP_GRAVITY_START; + } + + + } + else DBG("%s() %i not found head !!!!\n", __func__, i ); + } + } + + papp = panel->apps_start_head; + + offset = panel->margin_start; + + DBG("%s() start list:\n", __func__ ); + + while( papp != NULL) + { + + DBG("%s() %s moving to %i\n", __func__, papp->name, offset ); + + if (PANEL_IS_VERTICAL(panel)) + papp->y = offset; + else + papp->x = offset; + + papp->offset = offset; + + XMoveWindow(panel->dpy, papp->win, papp->x, papp->y); + + panel_app_deliver_config_event(panel, papp); + + offset += ( panel_app_get_size(panel, papp) + panel->padding ); + + papp = papp->next; + } + + papp = panel->apps_end_head; + + DBG("%s() end list:\n", __func__ ); + + if (papp) + { + offset = ( PANEL_IS_VERTICAL(panel) ? panel->h : panel->w ) - panel->margin_end; + + while( papp != NULL) + { + offset -= ( panel_app_get_size(panel, papp) + panel->padding ); + + DBG("%s() %s moving to %i\n", __func__, papp->name, offset ); + + if (PANEL_IS_VERTICAL(panel)) + papp->y = offset; + else + papp->x = offset; + + papp->offset = offset; + + XMoveWindow(panel->dpy, papp->win, papp->x, papp->y); + + panel_app_deliver_config_event(panel, papp); + + papp = papp->next; + } + } + + session_save(panel); + panel_menu_update_remove_items(panel); + panel_update_client_list_prop(panel); + +} + +MBPanel +*panel_init(int argc, char *argv[]) +{ + int panel_length; + XGCValues gv; + XSetWindowAttributes dattr; + unsigned long dattr_flags = CWBackPixel; + XSizeHints size_hints; + + unsigned long wm_struct_vals[4]; + + char *geometry_str = NULL; + char *color_def = NULL; + char *bg_pixmap_def = NULL; + char *display_name = (char *)getenv("DISPLAY"); + + char tray_atom_spec[128] = { 0 }; + char tray_id_env_str[16] = { 0 }; + + /* MBPanelApp *papp_menu_button = NULL; */ + MBPanel *panel; + + int panel_border_sz = 0; + char *want_trans = NULL; + + char win_name[64] = { 0 }; + /* +#ifdef USE_XFT + XRenderColor colortmp; +#endif + */ + + int i = 0, j = 0; + + struct { + char *name; + MBPanelOrientation orientation; + } orientation_lookup[] = { + { "north", North }, + { "south", South }, + { "east", East }, + { "west", West }, + { NULL, North } + }; + + XSetErrorHandler(util_handle_xerror); + + panel = NEW(MBPanel); + memset(panel, sizeof(MBPanel), 0); + + /* defualts */ + + panel->padding = 2; + panel->use_session = True; + panel->use_alt_session_defaults = False; + panel->use_menu = True; + panel->click_time = 400; + panel->use_overide_wins = False; + panel->orientation = South; + panel->system_tray_id = 0; + panel->use_flip = True; + panel->default_panel_size = 0; + panel->session_cur_gravity = PAPP_GRAVITY_START; + panel->theme_name = NULL; + panel->theme_path = NULL; + panel->apps_start_head = NULL; + panel->apps_end_head = NULL; + panel->ignore_next_config = False; + panel->margin_start = 2; + panel->margin_end = 2; + panel->bg_spec = NULL; + panel->want_titlebar_dest = False; + + for (i = 1; i < argc; i++) { + if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + display_name = argv[i]; + continue; + } + if (!strcmp ("--no-session", argv[i]) || !strcmp ("-ns", argv[i])) { + panel->use_session = False; + continue; + } + if (!strcmp ("--titlebar", argv[i]) || !strcmp ("-tb", argv[i])) { + panel->want_titlebar_dest = True; + continue; + } + + if (!strcmp ("--default-apps", argv[i]) || !strcmp ("-da", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + if (!strcmp(argv[i], "none")) + session_set_defaults(panel, strdup("")); + else + session_set_defaults(panel, argv[i]); + continue; + } + + if (!strcmp ("--no-menu", argv[i]) || !strcmp ("-nm", argv[i])) { + panel->use_menu = False; + continue; + } + if (!strcmp ("--overide-bubbles", argv[i]) || !strcmp ("-o", argv[i])) { + panel->use_overide_wins = True; + continue; + } + if (!strcmp ("--no-flip", argv[i])) { + panel->use_flip = False; + continue; + } + if (!strcmp ("--bgcolor", argv[i]) || !strcmp ("-c", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + color_def = argv[i]; + continue; + } + if (strstr (argv[i], "-geometry") || !strcmp ("-g", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + fprintf(stderr,"mbdock: -geometry is depreciated, please consider --size and --orientation instead\n"); + geometry_str = argv[i]; + continue; + } + if (strstr (argv[i], "--size") || !strcmp ("-s", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + panel->default_panel_size = atoi(argv[i]); + if (panel->default_panel_size < 1) panel_usage (argv[0]); + continue; + } + if (strstr (argv[i], "--margin-start")) { + if (++i>=argc) panel_usage (argv[0]); + panel->margin_start = atoi(argv[i]); + if (panel->margin_start < 1) panel_usage (argv[0]); + continue; + } + if (strstr (argv[i], "--margin-end")) { + if (++i>=argc) panel_usage (argv[0]); + panel->margin_end = atoi(argv[i]); + if (panel->margin_end < 1) panel_usage (argv[0]); + continue; + } + if (!strcmp ("--bgpixmap", argv[i]) || !strcmp ("-b", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + bg_pixmap_def = argv[i]; + continue; + } + if (!strcmp ("--bgtrans", argv[i]) || !strcmp ("-bt", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + want_trans = argv[i]; + continue; + } + if (!strcmp ("--id", argv[i]) || !strcmp ("-i", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + panel->system_tray_id = atoi(argv[i]); + continue; + + } + if (!strcmp ("--border", argv[i])) { + if (++i>=argc) panel_usage (argv[0]); + panel_border_sz = atoi(argv[i]); + continue; + } + if (!strcmp ("--orientation", argv[i])) { + Bool found = False; + if (++i>=argc) panel_usage (argv[0]); + j = 0; + while (orientation_lookup[j].name != NULL) + { + if (!strcasecmp(orientation_lookup[j].name, argv[i])) + { + panel->orientation = orientation_lookup[j].orientation; + found = True; + } + j++; + } + if (found) continue; + } + panel_usage(argv[0]); + } + + if ((panel->dpy = XOpenDisplay(display_name)) == NULL) + { + fprintf(stderr, "%s: failed to open display", argv[0]); + exit(1); + } + + panel->screen = DefaultScreen(panel->dpy); + panel->win_root = RootWindow(panel->dpy, panel->screen); + + if (panel->default_panel_size == 0) + panel->default_panel_size + = ( DisplayHeight(panel->dpy, panel->screen) > 320 ) ? 36 : 20; + + panel->x = 0; + panel->y = 0; + panel->h = panel->default_panel_size; + panel->w = DisplayWidth(panel->dpy, panel->screen); + + switch (panel->orientation) + { + case South: + panel->y = DisplayHeight(panel->dpy, panel->screen) - panel->h; + break; + case North: + break; + case West: + panel->w = panel->default_panel_size; + panel->h = DisplayHeight(panel->dpy, panel->screen); + break; + case East: + panel->w = panel->default_panel_size; + panel->h = DisplayHeight(panel->dpy, panel->screen); + panel->x = DisplayWidth(panel->dpy, panel->screen) - panel->w; + break; + } + + panel->bg_pxm = None; + + if ( PANEL_IS_VERTICAL(panel) ) + panel_length = panel->h; + else + panel_length = panel->w; + + /* Clip margin values to 40% */ + if (panel->margin_start > ( ( panel_length * 40 ) / 100 ) + || panel->margin_end > ( ( panel_length * 40 ) / 100 )) + { + fprintf(stderr, "mbdock: Panel margins too large. clipping.\n"); + panel->margin_start = 2; + panel->margin_end = 2; + } + + if (geometry_str) + XParseGeometry(geometry_str, &panel->x, &panel->y, &panel->w, &panel->h); + + /* XXX + * Lots of atoms now, move to xinternatoms call ... + */ + panel->atoms[0] = XInternAtom(panel->dpy, "_NET_WM_WINDOW_TYPE" ,False); + + panel->atoms[1] = XInternAtom(panel->dpy, "_NET_WM_WINDOW_TYPE_DOCK",False); + + + panel->atoms[3] = XInternAtom(panel->dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + panel->atoms[4] = XInternAtom(panel->dpy, "_XEMBED_INFO", False); + panel->atoms[5] = XInternAtom(panel->dpy, "_XEMBED", False); + panel->atoms[6] = XInternAtom(panel->dpy, "MANAGER", False); + + panel->atoms[7] = XInternAtom(panel->dpy, "_MB_DOCK_ALIGN", False); + panel->atoms[8] = XInternAtom(panel->dpy, "_MB_DOCK_ALIGN_EAST", False); + + panel->atoms[9] = XInternAtom(panel->dpy, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); + panel->atoms[10] = XInternAtom(panel->dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); + + panel->atoms[11] = XInternAtom(panel->dpy, "WM_PROTOCOLS", False); + panel->atoms[12] = XInternAtom(panel->dpy, "WM_DELETE_WINDOW", False); + + panel->atoms[13] = XInternAtom(panel->dpy, "_MB_THEME", False); + + panel->atoms[14] = XInternAtom(panel->dpy, "_MB_PANEL_TIMESTAMP", False); + + panel->atoms[15] = XInternAtom(panel->dpy, "_NET_WM_STRUT", False); + + panel->atoms[16] = XInternAtom(panel->dpy, "_MB_PANEL_BG", False); + + panel->atoms[17] = XInternAtom(panel->dpy, "WM_CLIENT_LEADER", False); + + panel->atoms[18] = XInternAtom(panel->dpy, "_NET_WM_ICON", False); + + panel->atoms[ATOM_NET_WM_PID] + = XInternAtom(panel->dpy, "_NET_WM_PID", False); + + panel->atoms[ATOM_XROOTPMAP_ID] + = XInternAtom(panel->dpy, "_XROOTPMAP_ID", False); + + panel->atoms[ATOM_NET_SYSTEM_TRAY_ORIENTATION] + = XInternAtom(panel->dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + + panel->atoms[ATOM_MB_THEME_NAME] + = XInternAtom(panel->dpy, "_MB_THEME_NAME", False); + + panel->atoms[ATOM_MB_COMMAND] + = XInternAtom(panel->dpy, "_MB_COMMAND", False); + + panel->atoms[ATOM_NET_WM_NAME] + = XInternAtom(panel->dpy, "_NET_WM_NAME", False); + + panel->atoms[ATOM_UTF8_STRING] + = XInternAtom(panel->dpy, "UTF8_STRING", False); + + panel->atoms[ATOM_NET_CLIENT_LIST] + = XInternAtom(panel->dpy, "_NET_CLIENT_LIST", False); + + panel->atoms[ATOM_NET_WM_STATE] + = XInternAtom(panel->dpy, "_NET_WM_STATE", False); + + panel->atoms[ATOM_NET_WM_STATE_TITLEBAR] + = XInternAtom(panel->dpy, "_MB_WM_STATE_DOCK_TITLEBAR", False); + + panel->atoms[ATOM_MB_SYSTEM_TRAY_CONTEXT] + = XInternAtom(panel->dpy, "_MB_SYSTEM_TRAY_CONTEXT", False); + + panel->atoms[ATOM_MB_REQ_CLIENT_ORDER] + = XInternAtom(panel->dpy, "_MB_REQ_CLIENT_ORDER", False); + + + /* Set selection atom */ + snprintf(tray_atom_spec, 128,"_NET_SYSTEM_TRAY_S%i", panel->system_tray_id); + + panel->atoms[ATOM_SYSTEM_TRAY] + = XInternAtom(panel->dpy, tray_atom_spec, False); + + snprintf(tray_id_env_str, 16, "%i", panel->system_tray_id); + setenv("SYSTEM_TRAY_ID", tray_id_env_str, 1); + + snprintf(win_name, 64, "Panel %i", panel->system_tray_id); + + panel->pb = mb_pixbuf_new(panel->dpy, panel->screen); + + gv.graphics_exposures = False; + gv.function = GXcopy; + gv.foreground = WhitePixel(panel->dpy, panel->screen); + + panel->gc = XCreateGC(panel->dpy, panel->win_root, + GCGraphicsExposures|GCFunction|GCForeground, &gv); + + dattr.background_pixel = panel->xcol.pixel; + + if (geometry_str) + { + /* Make the window overide redirect - kind of evil hack for now */ + dattr_flags = CWBackPixel|CWOverrideRedirect; + dattr.override_redirect = True; + } + + panel->win = XCreateWindow(panel->dpy, panel->win_root, + panel->x, panel->y, + panel->w, panel->h, + panel_border_sz, + CopyFromParent, + CopyFromParent, + CopyFromParent, + dattr_flags, + &dattr); + + size_hints.flags = PPosition | PSize | PMinSize; + size_hints.x = panel->x; + size_hints.y = panel->y; + size_hints.width = panel->w; + size_hints.height = panel->h; + size_hints.min_width = panel->w; + size_hints.min_height = panel->h; + + XSetStandardProperties(panel->dpy, panel->win, win_name, + win_name, 0, argv, argc, &size_hints); + + /* + XXX For now the menu popup button is removed. + If goes down well, need to rip old code out. + + panel_menu_button_create(panel); + + */ + + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_WM_WINDOW_TYPE], XA_ATOM, 32, + PropModeReplace, + (unsigned char *) &panel->atoms[ATOM_WM_WINDOW_TYPE_DOCK], + 1); + + if (panel->want_titlebar_dest) + { + panel->use_flip = False; + + XChangeProperty(panel->dpy, panel->win, + panel->atoms[ATOM_NET_WM_STATE], XA_ATOM, 32, + PropModeReplace, + (unsigned char *) &panel->atoms[ATOM_NET_WM_STATE_TITLEBAR], 1); + + panel->orientation = North; + } + + /* Set our ewmh reserved space XXX reset this when we hide */ + wm_struct_vals[0] = ( panel->orientation == West ) ? panel->w : 0; + wm_struct_vals[1] = ( panel->orientation == East ) ? panel->w : 0; + wm_struct_vals[2] = ( panel->orientation == North ) ? panel->h : 0; + wm_struct_vals[3] = ( panel->orientation == South ) ? panel->h : 0; + + XChangeProperty(panel->dpy, panel->win, panel->atoms[ATOM_NET_WM_STRUT], + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)wm_struct_vals, 4); + + panel->msg_queue_start = NULL; + panel->msg_queue_end = NULL; + panel->msg_win = None; + + panel->msg_col + = mb_col_new_from_spec(panel->pb, DEFAULT_MSG_BGCOL); + panel->msg_urgent_col + = mb_col_new_from_spec(panel->pb, DEFAULT_MSG_BGURGCOL); + panel->msg_fg_col + = mb_col_new_from_spec(panel->pb, DEFAULT_MSG_FGCOL); + panel->msg_link_col + = mb_col_new_from_spec(panel->pb, "blue"); + + panel->msg_gc = XCreateGC(panel->dpy, panel->win_root, + GCGraphicsExposures|GCFunction|GCForeground, &gv); + + panel->msg_font = mb_font_new_from_string(panel->dpy, MB_MSG_FONT); + + panel->next_click_is_not_double = True; + panel->is_hidden = False; + + XSetWMProtocols(panel->dpy, panel->win , &panel->atoms[11], 2); + + panel->mbmenu = NULL; + panel_menu_init(panel); + + G_panel = panel; /* global for sig handlers :( */ + panel->reload_pending = False; + + util_install_signal_handlers(); + + /* Set the theme etc */ + + panel->use_themes = True; + + if (color_def != NULL) + { + panel_set_bg(panel, BG_SOLID_COLOR, color_def); + mb_menu_set_col(panel->mbmenu, MBMENU_SET_BG_COL, color_def); + panel->use_themes = False; + } + else if (bg_pixmap_def) + { + panel_set_bg(panel, BG_PIXMAP, bg_pixmap_def); + panel->use_themes = False; + } + else if (want_trans) + { + panel->bg_trans = strdup(want_trans); + + panel_set_bg(panel, BG_TRANS, want_trans ); + panel->use_themes = False; + } + else + { + if (!panel_set_theme_from_root_prop(panel)) + { + panel_set_bg(panel, BG_SOLID_COLOR, DEFAULT_COLOR_SPEC ); + mb_menu_set_col(panel->mbmenu, MBMENU_SET_BG_COL, DEFAULT_COLOR_SPEC ); + } + } + + panel->click_x = 0; + panel->click_y = 0; + +#ifdef USE_XSETTINGS + + /* This will trigger callbacks instantly so called last */ + + panel->xsettings_client = xsettings_client_new(panel->dpy, panel->screen, + panel_xsettings_notify_cb, + NULL, + (void *)panel ); +#endif + + + /* Below line is there to get the menu btton on the right side */ + panel->session_preexisting_lock = True; + + return panel; +} + +int main(int argc, char *argv[]) +{ + MBPanel *panel; + +#if ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, DATADIR "/locale"); + bind_textdomain_codeset (PACKAGE, "UTF-8"); + textdomain (PACKAGE); +#endif + + panel = panel_init(argc, argv); + + /* Attempt to own the system tray selection */ + if (!XGetSelectionOwner(panel->dpy, panel->atoms[ATOM_SYSTEM_TRAY])) + { + XSetSelectionOwner(panel->dpy, panel->atoms[ATOM_SYSTEM_TRAY], + panel->win, CurrentTime); + } else { + fprintf(stderr, "Panel already exists. aborting. Try running mbdock with the --id switch.\n"); + exit(0); + } + + /* Announce to any clients that are interested that we have it */ + panel_send_manage_message( panel ); + + panel_orientation_set_hint (panel); + + XMapWindow (panel->dpy, panel->win); + + session_init (panel); + + panel_main(panel); + + return 0; +} + diff --git a/src/panel.h b/src/panel.h new file mode 100644 index 0000000..79da505 --- /dev/null +++ b/src/panel.h @@ -0,0 +1,312 @@ +#ifndef _DOCK_H_ +#define _DOCK_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <signal.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/xpm.h> +#include <X11/Xmd.h> +#include <X11/extensions/shape.h> +#include <X11/cursorfont.h> + +#include <libmb/mb.h> + +#ifdef USE_XSETTINGS +#include <xsettings-client.h> +#endif + +#define NEW(OBJ) ((OBJ *)(malloc(sizeof(OBJ)))) + +#ifdef DEBUG +#define DBG(txt, args... ) fprintf(stderr, "MB-PANEL-DEBUG: " txt , ##args ) +#else +#define DBG(txt, args... ) /* nothing */ +#endif + +#define SWAP(a,b) { \ + (a)^=(b); \ + (b)^=(a); \ + (a)^=(b); \ + } + +#define PANEL_IS_VERTICAL(p) ((p)->orientation == East || (p)->orientation == West) + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define ATOM_WM_WINDOW_TYPE 0 +#define ATOM_WM_WINDOW_TYPE_DOCK 1 +#define ATOM_WM_WINDOW_TYPE_SPLASH 10 +#define ATOM_SYSTEM_TRAY 2 +#define ATOM_SYSTEM_TRAY_OPCODE 3 +#define ATOM_XEMBED_INFO 4 +#define ATOM_XEMBED_MESSAGE 5 +#define ATOM_MANAGER 6 +#define ATOM_MB_DOCK_ALIGN 7 +#define ATOM_MB_DOCK_ALIGN_EAST 8 + +#define ATOM_NET_SYSTEM_TRAY_MESSAGE_DATA 9 + +#define ATOM_WM_PROTOCOLS 11 +#define ATOM_WM_DELETE_WINDOW 12 + +#define ATOM_MB_THEME 13 +#define ATOM_MB_PANEL_BG 16 +#define ATOM_MB_DOCK_TIMESTAMP 14 +#define ATOM_NET_WM_STRUT 15 +#define ATOM_WM_CLIENT_LEADER 17 +#define ATOM_NET_WM_ICON 18 +#define ATOM_NET_WM_PID 19 +#define ATOM_XROOTPMAP_ID 20 +#define ATOM_NET_SYSTEM_TRAY_ORIENTATION 21 +#define ATOM_MB_THEME_NAME 22 +#define ATOM_MB_COMMAND 23 +#define ATOM_NET_WM_NAME 24 +#define ATOM_UTF8_STRING 25 +#define ATOM_NET_CLIENT_LIST 26 + +#define ATOM_NET_WM_STATE 27 +#define ATOM_NET_WM_STATE_TITLEBAR 28 + +#define ATOM_MB_SYSTEM_TRAY_CONTEXT 29 +#define ATOM_MB_REQ_CLIENT_ORDER 30 + +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 + +/* ID's for various MB COMMAND X Messages */ + +#define MB_CMD_PANEL_TOGGLE_VISIBILITY 1 +#define MB_CMD_PANEL_SIZE 2 +#define MB_CMD_PANEL_ORIENTATION 3 + +#define MB_PANEL_ORIENTATION_NORTH 1 +#define MB_PANEL_ORIENTATION_EAST 2 +#define MB_PANEL_ORIENTATION_SOUTH 3 +#define MB_PANEL_ORIENTATION_WEST 4 + + +#define SESSION_TIMEOUT 10 /* 5 second session timeout */ + +#define DBL_CLICK_TIME 200 + + +#define DEFAULT_COLOR_SPEC "#e2e2de" /* Same as gnome ? */ +#define MB_MSG_FONT "Sans-8:bold" + +enum { + BG_SOLID_COLOR, + BG_PIXMAP, + BG_TRANS +}; + +typedef enum { + PAPP_GRAVITY_START, + PAPP_GRAVITY_END, +} PanelAppGravity; + +typedef enum { + North = 1, + East, + South, + West +} MBPanelOrientation; + + +typedef struct _panel_app { + + Window win; + unsigned char *name; + int x; + int y; + int w; + int h; + + int offset; + + Bool mapped; + + struct _panel_app *next; + + char *cmd_str; + + struct _panel *panel; + + Bool ignore; /* set so client cant be removed */ + int ignore_unmap; + + PanelAppGravity gravity; + + MBPixbufImage *icon; + +} MBPanelApp; + +typedef struct _message_queue { + + MBPanelApp *sender; + unsigned long starttime; + int timeout; + int total_msg_length; + int current_msg_length; + int id; + unsigned char *data; + + Bool has_extra_context; + unsigned char *extra_context_data; + + Bool pending; /* Set to true if all data */ + + struct _message_queue *next; + +} MBPanelMessageQueue; + + +typedef struct _panel { + + /* General */ + + Display *dpy; + int screen; + MBPixbuf *pb; + Window win, win_root; + GC gc, band_gc; + XColor xcol; + + int x,y,w,h; + + MBPanelApp *apps_start_head; + MBPanelApp *apps_end_head; + + Atom atoms[31]; + int padding; + int margin_start; + int margin_end; + + /* Message windows */ + + struct _message_queue *msg_queue_start; + struct _message_queue *msg_queue_end; + + Window msg_win; + unsigned long msg_starttime; + int msg_timeout; + MBPanelApp *msg_win_sender; + Bool msg_has_context; + int msg_context_y1, msg_context_y2; + + GC msg_gc; + + MBColor *msg_col; + MBColor *msg_urgent_col; + MBColor *msg_fg_col; + MBColor *msg_link_col; + + MBFont *msg_font; + + + /* Various state bits */ + + Bool use_flip; + Bool use_session; + Bool use_alt_session_defaults; + + Bool use_overide_wins; + Bool reload_pending; + + MBPanelOrientation orientation; + + int system_tray_id; + + Bool use_themes; + char *theme_name; + char *theme_path; + + Bool want_titlebar_dest; + + Bool ignore_next_config; + + int default_panel_size; + + /* Session */ + + Bool session_preexisting_lock; + int session_init_offset; + char session_entry_cur[512]; + pid_t session_needed_pid; + time_t session_start_time; + FILE *session_fp; + PanelAppGravity session_cur_gravity; + Bool session_run_first_time; + char *session_defaults_cur_pos; + + Window last_click_window; + Time last_click_time; + Bool next_click_is_not_double; + Bool is_hidden; + + /* Background */ + + XColor bg_col; + Pixmap bg_tile; + Pixmap bg_pxm; + char *bg_spec; + int bg_type; + + /* Popup menu */ + + MBMenu* mbmenu; + Bool use_menu; + MBMenuMenu *remove_menu; + + /* co-ords of where mouse click happened for menu */ + int click_x; + int click_y; + Time click_time; + + + char *bg_trans; + long root_pixmap_id; + +#ifdef USE_XSETTINGS + XSettingsClient *xsettings_client; +#endif + +} MBPanel; + +void +panel_handle_full_panel (MBPanel *panel, MBPanelApp *bad_papp); + +void +panel_toggle_visibilty(MBPanel *panel); + +void +panel_change_orientation (MBPanel *panel, + MBPanelOrientation new_orientation, + int dpy_w, + int dpy_h); + +void +panel_update_client_list_prop (MBPanel *panel); + +void +panel_reorder_apps(MBPanel *panel); + +#include "panel_menu.h" +#include "panel_util.h" +#include "panel_app.h" +#include "session.h" + +#endif diff --git a/src/panel_app.c b/src/panel_app.c new file mode 100644 index 0000000..a3d020b --- /dev/null +++ b/src/panel_app.c @@ -0,0 +1,597 @@ +#include "panel_app.h" + +MBPanelApp* +panel_app_list_get_prev (MBPanel *panel, + MBPanelApp *papp, + MBPanelApp **list_head) +{ + MBPanelApp *tmp = *list_head; + + if (tmp == NULL || papp == tmp) return NULL; + + while (tmp->next != NULL) + { + if (tmp->next == papp) return tmp; + tmp = tmp->next; + } + + return NULL; +} + +MBPanelApp* +panel_app_list_get_last (MBPanel *panel, + MBPanelApp *list_head) +{ + MBPanelApp *tmp = list_head; + + if (tmp == NULL) return NULL; + + while (tmp->next != NULL) tmp = tmp->next; + + return tmp; +} + +MBPanelApp* +panel_app_list_prepend(MBPanel *panel, + MBPanelApp *list_to_append_to, + MBPanelApp *papp_new) +{ + papp_new->next = list_to_append_to; + return papp_new; +} + +void +panel_app_list_append (MBPanel *panel, + MBPanelApp **list_to_append_to, + MBPanelApp *new_client) +{ + MBPanelApp *tmp = NULL; + if (*list_to_append_to == NULL) + { + *list_to_append_to = new_client; + } + else + { + tmp = *list_to_append_to; + while ( tmp->next != NULL ) tmp = tmp->next; + tmp->next = new_client; + } + + new_client->next = NULL; +} + +void /* XXX Can probably go */ +panel_app_list_insert_after(MBPanel *panel, + MBPanelApp *papp, + MBPanelApp *new_papp) +{ + MBPanelApp *tmp; + tmp = papp->next; + papp->next = new_papp; + new_papp->next = tmp; +} + +void +panel_app_list_remove (MBPanel *panel, + MBPanelApp *papp, + MBPanelApp **list_head) +{ + MBPanelApp *prev_papp = panel_app_list_get_prev(panel, papp, list_head); + + if (prev_papp == NULL) + { + *list_head = papp->next; + } + else + { + prev_papp->next = papp->next; + } + return; +} + +void +panel_app_name_get(MBPanel *panel, + MBPanelApp *papp) +{ + Atom type; + int format; + long bytes_after; + long n_items; + int result; + + result = XGetWindowProperty (panel->dpy, papp->win, + panel->atoms[ATOM_NET_WM_NAME], + 0, 1024L, + False, panel->atoms[ATOM_UTF8_STRING], + &type, &format, &n_items, + &bytes_after, (unsigned char **)&papp->name); + + if (result != Success + || papp->name == NULL + || type != panel->atoms[ATOM_UTF8_STRING] + || format != 8 + || n_items == 0) + { + if (papp->name) XFree (papp->name); + + XFetchName(panel->dpy, papp->win, (char **)&papp->name); + if (papp->name == NULL) { + XStoreName(panel->dpy, papp->win, "<unnamed>"); + XFetchName(panel->dpy, papp->win, (char **)&papp->name); + if (papp->name == NULL) + papp->name = strdup("<unnamed>"); + } + } +} + +Window +panel_app_get_client_leader_win(MBPanel *panel, MBPanelApp *papp) +{ + Atom realType; + unsigned long n; + unsigned long extra; + int format; + int status; + Window* value = NULL, win_result = None; + + status = XGetWindowProperty(panel->dpy, papp->win, + panel->atoms[ATOM_WM_CLIENT_LEADER], + 0L, 16L, + 0, XA_WINDOW, &realType, &format, + &n, &extra, (unsigned char **) &value); + if (status == Success && realType == XA_WINDOW + && format == 32 && n == 1 && value != NULL) + { + win_result = (Window) value[0]; + } + + if (value) XFree(value); + + return win_result; +} + +int* +panel_app_icon_prop_data_get(MBPanel *d, MBPanelApp *papp) +{ + Atom type; + int format; + long bytes_after; + unsigned char *data = NULL; + long n_items; + int result; + + result = XGetWindowProperty (d->dpy, papp->win, + d->atoms[ATOM_NET_WM_ICON], + 0, 100000L, + False, XA_CARDINAL, + &type, &format, &n_items, + &bytes_after, (unsigned char **)&data); + + if (result != Success || data == NULL) + { + if (data) XFree (data); + DBG("%s() failed for %s (XID: %li)\n", __func__, papp->name, papp->win); + return NULL; + } + + return (int *)data; +} + +void +panel_app_add_start(MBPanel *panel, MBPanelApp *papp_new) +{ + MBPanelApp *papp_prev; + + papp_prev = panel_app_list_get_last(panel, panel->apps_start_head); + + panel_app_list_append (panel, &panel->apps_start_head, papp_new); + + if (papp_prev != NULL) + { + papp_new->offset = panel_app_get_offset(panel, papp_prev) + + panel_app_get_size(panel, papp_prev); + DBG("%s() got %i = %i + %i\n", __func__, papp_new->offset, + panel_app_get_offset(panel, papp_prev) , + panel_app_get_size(panel, papp_prev) ); + } + else + papp_new->offset = 0; + + papp_new->gravity = PAPP_GRAVITY_START; +} + +void +panel_app_add_end(MBPanel *panel, MBPanelApp *papp_new) +{ + MBPanelApp *papp_prev; + + papp_prev = panel_app_list_get_last(panel, panel->apps_end_head); + + panel_app_list_append (panel, &panel->apps_end_head, papp_new); + + if (papp_prev != NULL) + papp_new->offset = panel_app_get_offset(panel, papp_prev) + - panel_app_get_size(panel, papp_new); + else + papp_new->offset = ( PANEL_IS_VERTICAL(panel) ? panel->h : panel->w ) + - panel_app_get_size(panel, papp_new); + + papp_new->gravity = PAPP_GRAVITY_END; +} + +Bool +panel_app_check_for_space (MBPanel *panel, MBPanelApp *papp) +{ + MBPanelApp *papp_end = NULL, *papp_start = NULL; + + papp_end = panel_app_list_get_last(panel, panel->apps_end_head); + papp_start = panel_app_list_get_last(panel, panel->apps_start_head); + + if (papp_end && papp_start && + panel_app_get_offset(panel, papp_start) + + panel_app_get_size(panel, papp_start) + > panel_app_get_offset(panel, papp_end)) + { + panel_handle_full_panel(panel, papp); + return False; + } + return True; +} + +MBPanelApp * +panel_app_new(MBPanel *panel, Window win, char *cmd_str) +{ + MBPanelApp *papp; + XWindowAttributes attr; + int padding = 0; + Bool add_at_start = False; + + papp = NEW(MBPanelApp); + + papp->next = NULL; + papp->win = win; + papp->panel = panel; + papp->ignore = False; + papp->ignore_unmap = 0; + papp->icon = NULL; + + /* XXX should check we actually get this */ + XGetWindowAttributes(panel->dpy, win, &attr); + + papp->w = attr.width; + papp->h = attr.height; + + if (session_preexisting_restarting(panel) && !panel->session_run_first_time) + { + if (panel->session_cur_gravity == PAPP_GRAVITY_START) + add_at_start = True; + } + else if ( ((attr.x < 0) && !PANEL_IS_VERTICAL(panel)) + || ((attr.y < 0) && PANEL_IS_VERTICAL(panel))) + { + add_at_start = True; + } + + if (add_at_start) + { + if (panel->apps_start_head == NULL) + padding = panel->margin_start; + else + padding = panel->padding; + + panel_app_add_start(panel, papp); + } + else + { + if (panel->apps_end_head == NULL) + padding = -1 * panel->margin_end; + else + padding = -1 * panel->padding; + + panel_app_add_end(panel, papp); + } + + DBG("%s() papp offset at %i\n", __func__, papp->offset ); + + panel_app_name_get(panel, papp); + + papp->cmd_str = cmd_str; + + if (panel->orientation == North || panel->orientation == South) + { + papp->h = panel->h - 4; + papp->y = (panel->h - papp->h) / 2; + papp->x = papp->offset + padding; + } + else + { + papp->w = panel->w - 4; + papp->x = (panel->w - papp->w) / 2; + papp->y = papp->offset + padding; + } + + if (!panel_app_check_for_space(panel, papp)) + return NULL; + + XResizeWindow(panel->dpy, papp->win, papp->w, papp->h); + XReparentWindow(panel->dpy, papp->win, panel->win, papp->x, papp->y); + panel_app_deliver_config_event(panel, papp); + + panel_update_client_list_prop (panel); + + return papp; +} + +void +panel_app_handle_configure_request(MBPanel *panel, XConfigureRequestEvent *ev) +{ + XWindowChanges xwc; + MBPanelApp *papp = NULL; + + papp = panel_app_get_from_window( panel, ev->window ); + + if (panel->is_hidden) return; + + if (papp != NULL) + { + DBG("%s() config req x: %i , y: %i w: %i h: %i for %s\n", + __func__, ev->x, ev->y, ev->width, ev->height, papp->name ); + + DBG("%s() panel is w: %i %i h:\n", + __func__, panel->w, panel->h ); + + if (panel->orientation == North || panel->orientation == South) + { + xwc.width = ev->width; + xwc.height = panel->h - 4; + + papp->y = (panel->h - papp->h) / 2; + + if (xwc.width != papp->w) /* Handle width changes */ + { + if (papp == panel_app_list_get_last(panel, panel->apps_end_head)) + { + panel_app_move_to(panel, papp, + papp->x - (xwc.width - papp->w)); + } + else if (papp == panel_app_list_get_last(panel, + panel->apps_start_head)) + { + panel_app_move_to(panel, papp, + papp->x + (xwc.width - papp->w)); + } + else + { + panel_apps_nudge (panel, papp->next, xwc.width - papp->w); + } + papp->w = xwc.width; + } + + xwc.x = papp->x; /* NOT allowed to move themselves */ + xwc.y = papp->y; + + } else { /* East / West orientated dock */ + + xwc.width = panel->w - 4; + xwc.height = ev->height; + + papp->x = (panel->w - papp->w) / 2; + xwc.x = papp->x; + xwc.y = papp->y; + + if (xwc.height != papp->h) /* Handle width changes */ + { + if (papp == panel_app_list_get_last(panel, panel->apps_end_head)) + { + panel_app_move_to(panel, papp, + papp->y - (xwc.height - papp->h)); + } + else if (papp == panel_app_list_get_last(panel, + panel->apps_start_head)) + { + panel_app_move_to(panel, papp, + papp->y + (xwc.height - papp->h)); + } + else + { + panel_apps_nudge (panel, papp->next, xwc.height - papp->h); + } + papp->h = xwc.height; + } + } + + xwc.border_width = 0; + xwc.sibling = None; + xwc.stack_mode = None; + + DBG("%s() setting x: %i , y: %i w: %i h: %i \n", + __func__, xwc.x, xwc.y, xwc.width, xwc.height ); + + XConfigureWindow(panel->dpy, papp->win, ev->value_mask, &xwc); + } +} + +void +panel_app_deliver_config_event(MBPanel *panel, MBPanelApp *papp) +{ + XConfigureEvent ce; + + if (panel->is_hidden) return; + + ce.type = ConfigureNotify; + ce.event = papp->win; + ce.window = papp->win; + + if (PANEL_IS_VERTICAL(panel)) + { + papp->x = (panel->w - papp->w) / 2; + ce.x = papp->x; + ce.y = papp->y; + ce.width = papp->w; + ce.height = papp->h; + } else { + papp->y = (panel->h - papp->h) / 2; + ce.x = papp->x; + ce.y = papp->y; // + panel->y; + ce.width = papp->w; + ce.height = papp->h; + } + + ce.border_width = 0; + ce.above = panel->win; + ce.override_redirect = 0; + + DBG("%s() delivering x: %i , y: %i w: %i h: %i name : %s\n", + __func__, ce.x, ce.y, ce.width, ce.height, papp->name ); + + XSendEvent(panel->dpy, papp->win, False, + StructureNotifyMask, (XEvent *)&ce); +} + +void +panel_app_move_to (MBPanel *panel, + MBPanelApp *papp, + int origin_offset) +{ + if (panel->orientation == North || panel->orientation == South) + { + papp->x = origin_offset; + papp->y = (panel->h - papp->h) / 2; + } + else + { + papp->y = origin_offset; + papp->x = (panel->w - papp->w) / 2; + } + + papp->offset = origin_offset; + + if (!panel_app_check_for_space(panel, papp)) + return; + + XMoveWindow(panel->dpy, papp->win, papp->x, papp->y); +} + +void +panel_apps_nudge (MBPanel *panel, + MBPanelApp *papp, + int amount) +{ + MBPanelApp *papp_cur = papp; + + while (papp_cur != NULL) + { + panel_app_move_to (panel, papp_cur, + panel_app_get_offset(panel, papp_cur) + amount); + papp_cur = papp_cur->next; + } +} + +void +panel_apps_rescale (MBPanel *panel, + MBPanelApp *papp) +{ + MBPanelApp *papp_cur = papp; + + while (papp_cur != NULL) + { + papp->h = panel->h - 4; + XResizeWindow(panel->dpy, papp->win, papp->w, papp->h); + panel_app_deliver_config_event(panel, papp); + papp_cur = papp_cur->next; + } +} + +void +panel_app_destroy (MBPanel *panel, + MBPanelApp *papp) +{ + MBMenuItem *tmp = NULL; + + if (!papp) return; + + /* remove popup menu entry XXX this functionaility should be in mbmenu */ + if (panel->remove_menu) + tmp = panel->remove_menu->items; + + while (tmp != NULL) + { + if ((MBPanelApp *)tmp->cb_data == papp) + { + mb_menu_item_remove(panel->mbmenu, panel->remove_menu, tmp); + break; + } + tmp = tmp->next_item; + } + + if (papp->gravity == PAPP_GRAVITY_START) + { + panel_apps_nudge(panel, papp->next, + -1 * panel_app_get_size(panel, papp)); + panel_app_list_remove(panel, papp, &panel->apps_start_head); + } + else + { + panel_apps_nudge(panel, papp->next, panel_app_get_size(panel, papp)); + panel_app_list_remove(panel, papp, &panel->apps_end_head); + } + + if (papp->name) XFree(papp->name); + + if (papp->cmd_str) free(papp->cmd_str); + + if (papp->icon) mb_pixbuf_img_free(panel->pb, papp->icon); + + free(papp); + + panel_update_client_list_prop (panel); +} + +/* Utilities */ + +int +panel_app_get_offset (MBPanel *panel, + MBPanelApp *papp) +{ + if (panel->orientation == East || panel->orientation == West) + return papp->y; + else + return papp->x; +} + +int +panel_app_get_size (MBPanel *panel, + MBPanelApp *papp) +{ + if (panel->orientation == East || panel->orientation == West) + return papp->h; + else + return papp->w; +} + +MBPanelApp* +panel_app_get_from_window (MBPanel *panel, + Window win) +{ + MBPanelApp *papp = panel->apps_start_head; + DBG("%s() called, looking for win %li\n", __func__, win); + + while( papp != NULL) + { + DBG("%s() check %s ( %li )\n", __func__, papp->name, papp->win); + if (papp->win == win) return papp; + papp = papp->next; + } + + papp = panel->apps_end_head; + + while( papp != NULL) + { + // DBG("%s() check %s ( %li )\n", __func__, papp->name, papp->win); + if (papp->win == win) return papp; + papp = papp->next; + } + + return NULL; +} + diff --git a/src/panel_app.h b/src/panel_app.h new file mode 100644 index 0000000..cb13deb --- /dev/null +++ b/src/panel_app.h @@ -0,0 +1,92 @@ +#ifndef _PANEL_APP_H_ +#define _PANEL_APP_H_ + +#include "panel.h" + +MBPanelApp* +panel_app_list_get_prev (MBPanel *panel, + MBPanelApp *papp, + MBPanelApp **list_head); +MBPanelApp* +panel_app_list_get_last (MBPanel *panel, + MBPanelApp *list_head); + + +MBPanelApp * +panel_app_list_prepend(MBPanel *panel, + MBPanelApp *list_to_append_to, + MBPanelApp *papp_new); + +/* +void +panel_app_list_prepend(MBPanel *panel, + MBPanelApp **list_to_append_to, + MBPanelApp *papp_new); +*/ + +void +panel_app_list_append (MBPanel *panel, + MBPanelApp **list_to_append_to, + MBPanelApp *new_client); + + +void panel_app_list_insert_after(MBPanel *panel, MBPanelApp *papp, + MBPanelApp *new_papp); + +void panel_app_list_remove (MBPanel *panel, + MBPanelApp *papp, + MBPanelApp **list_head ); + +void +panel_app_add_start(MBPanel *panel, MBPanelApp *papp_new); + +void +panel_app_add_end(MBPanel *panel, MBPanelApp *papp_new); + + +void panel_app_list_add(MBPanel *panel, MBPanelApp *papp_new); + +void panel_app_name_get(MBPanel *panel, MBPanelApp *papp); + +Window panel_app_get_client_leader_win(MBPanel *panel, MBPanelApp *papp); + +int* panel_app_icon_prop_data_get(MBPanel *d, MBPanelApp *papp); + +void panel_app_command_prop_get(MBPanel *panel, MBPanelApp *papp); + +Bool panel_app_get_command_str(MBPanel *panel, MBPanelApp *papp, + char **result); + +MBPanelApp* panel_app_get_from_window(MBPanel *panel, Window win); + +MBPanelApp* panel_app_new(MBPanel *panel, + Window win, + char *cmd ); + +void panel_app_handle_configure_request(MBPanel *panel, + XConfigureRequestEvent *ev); + +void panel_app_deliver_config_event(MBPanel *panel, MBPanelApp *papp); + +void +panel_apps_rescale (MBPanel *panel, + MBPanelApp *papp); + +void +panel_apps_nudge (MBPanel *panel, + MBPanelApp *papp, + int amount); + +void +panel_app_move_to(MBPanel *panel, MBPanelApp *papp, int origin_offset); + +void +panel_app_destroy(MBPanel *panel, MBPanelApp *papp); + +int +panel_app_get_offset(MBPanel *panel, MBPanelApp *papp); + +int +panel_app_get_size(MBPanel *panel, MBPanelApp *papp); + +#endif diff --git a/src/panel_menu.c b/src/panel_menu.c new file mode 100644 index 0000000..fbb6169 --- /dev/null +++ b/src/panel_menu.c @@ -0,0 +1,276 @@ +#include "panel_menu.h" + +#ifdef USE_PNG +#define FOLDER_IMG "mbfolder.png" +#define ADD_IMG "mbadd.png" +#define REMOVE_IMG "mbremove.png" +#define HIDE_IMG "mbdown.png" +#else +#define FOLDER_IMG "mbfolder.xpm" +#define ADD_IMG "mbadd.xpm" +#define REMOVE_IMG "mbremove.xpm" +#define HIDE_IMG "mbdown.xpm" +#endif + +void panel_menu_exec_cb(MBMenuItem *item) +{ + char *cmd = strdup(item->info); + + util_fork_exec(cmd); + free(cmd); +} + +void panel_menu_exit_cb(MBMenuItem *item) +{ + /* This should only be called by the menu. + As we now delete the session file. So its not used again. + */ + MBPanel *panel = (MBPanel *)item->cb_data; + session_destroy(panel); + + util_cleanup_children(0); +} + +void panel_menu_hide_cb(MBMenuItem *item) +{ + MBPanel *panel = (MBPanel *)item->cb_data; + panel_toggle_visibilty(panel); +} + +void panel_menu_kill_cb(MBMenuItem *item) +{ + MBPanelApp *papp = (MBPanelApp *)item->cb_data; + + XGrabServer(papp->panel->dpy); + XKillClient(papp->panel->dpy, papp->win); + session_save(papp->panel); + XUngrabServer(papp->panel->dpy); +} + +void +panel_menu_update_remove_items(MBPanel *panel) +{ + int *icon_data = NULL; + MBMenuItem *menu_item; + MBPanelApp *papp = NULL; + MBPanelApp *papp_heads[] = { panel->apps_start_head, + panel->apps_end_head, + NULL }; + int i = 0; + + if (panel->remove_menu == NULL) + { + char *icon_path = NULL; + + icon_path = mb_dot_desktop_icon_get_full_path (panel->theme_name, + 16, REMOVE_IMG ); + + panel->remove_menu = mb_menu_add_path(panel->mbmenu, _("Remove"), + icon_path, + MBMENU_PREPEND); + if (icon_path) free(icon_path); + } + + /* Remove all items then readd so order matches panel */ + + if (panel->remove_menu->items) + { + MBMenuItem *tmp_item = NULL; + + menu_item = panel->remove_menu->items; + + while (menu_item != NULL) + { + tmp_item = menu_item->next_item; + mb_menu_item_remove(panel->mbmenu, panel->remove_menu, menu_item); + menu_item = tmp_item; + } + } + + + while (i < 2) + { + papp = papp_heads[i]; + + if (i == 1) + papp = panel_app_list_get_last(panel, panel->apps_end_head); + + while( papp != NULL) + { + if (!papp->ignore) + { + menu_item = mb_menu_add_item_to_menu(panel->mbmenu, + panel->remove_menu, + papp->name, NULL, + papp->name, + panel_menu_kill_cb, + (void *)papp, + MBMENU_NO_SORT); + + if (!papp->icon) + { + if ((icon_data = panel_app_icon_prop_data_get(panel, papp)) + != NULL ) + { + char *p; + int j; + + DBG("%s() Got icon data (size: %i x %i)\n", __func__, + icon_data[0], icon_data[1] ); + + papp->icon = mb_pixbuf_img_new(panel->pb, + icon_data[0], icon_data[1] ); + p = papp->icon->rgba; + + for (j =0 ; j < (icon_data[0]*icon_data[1]); j++) + { + *p++ = (icon_data[j+2] >> 16) & 0xff; + *p++ = (icon_data[j+2] >> 8) & 0xff; + *p++ = icon_data[j+2] & 0xff; + *p++ = icon_data[j+2] >> 24; + } + + XFree(icon_data); + } + } + + if (papp->icon) + mb_menu_item_icon_set(panel->mbmenu, menu_item, papp->icon); + } + + if (i == 1) + { + papp = panel_app_list_get_prev (panel, papp, + &panel->apps_end_head); + } + else papp = papp->next; + } + i++; + } + +} + +void +panel_menu_init(MBPanel *panel) +{ + MBMenuMenu *m, *menu_launchers; + char orig_wd[256] = { 0 }; + struct dirent *dir_entry; + char *icon_path = NULL; + DIR *dp; + + if (panel->mbmenu == NULL) + { + panel->mbmenu = mb_menu_new(panel->dpy, panel->screen ); + mb_menu_set_icon_size(panel->mbmenu, 16); + } + else mb_menu_free(panel->mbmenu); /* XXX should be mb_menu_empty */ + + icon_path = mb_dot_desktop_icon_get_full_path (panel->theme_name, + 16, ADD_IMG ); + + m = mb_menu_add_path(panel->mbmenu, _("Add"), icon_path, + MBMENU_NO_SORT ); + + if (icon_path) free(icon_path); + + icon_path = mb_dot_desktop_icon_get_full_path (panel->theme_name, + 16, FOLDER_IMG ); + + menu_launchers = mb_menu_add_path(panel->mbmenu, "Add/Launchers", + icon_path, MBMENU_NO_SORT ); + + if (icon_path) free(icon_path); + + if (getcwd(orig_wd, 255) == (char *)NULL) + { + printf("Cant get current directory\n"); + exit(0); + } + + if ((dp = opendir(DATADIR "/applications")) != NULL) + { + chdir(DATADIR "/applications"); + + while((dir_entry = readdir(dp)) != NULL) + { + struct stat stat_info; + stat(dir_entry->d_name, &stat_info); + if (!(stat_info.st_mode & S_IFDIR)) + { + MBDotDesktop *ddentry = NULL; + ddentry = mb_dotdesktop_new_from_file(dir_entry->d_name); + if (ddentry + && mb_dotdesktop_get(ddentry, "Type") + && mb_dotdesktop_get(ddentry, "Name") + && mb_dotdesktop_get(ddentry, "Exec") + ) + { + + char *png_path = NULL; + unsigned char *icon_str = mb_dotdesktop_get(ddentry, "Icon"); + + png_path = mb_dot_desktop_icon_get_full_path ( + panel->theme_name, + 16, icon_str ); + + if (!strcmp(mb_dotdesktop_get(ddentry, "Type"), "PanelApp")) + { + mb_menu_add_item_to_menu(panel->mbmenu, + m, + mb_dotdesktop_get(ddentry, + "Name"), + png_path, + mb_dotdesktop_get(ddentry, + "Exec"), + panel_menu_exec_cb, + (void *)panel, 0); + } else { + char launcher_exec_str[256] = { 0 }; + snprintf(launcher_exec_str, 256, + "monolaunch -o -1 --desktop %s/%s", + DATADIR "/applications", dir_entry->d_name); + mb_menu_add_item_to_menu(panel->mbmenu, + menu_launchers, + mb_dotdesktop_get(ddentry, + "Name"), + png_path, + launcher_exec_str, + panel_menu_exec_cb, + (void *)panel, 0); + + } + if (png_path) free(png_path); + mb_dotdesktop_free(ddentry); + } + } + } + closedir(dp); + } + else fprintf(stderr, "failed to open %s\n", DATADIR "/applications"); + + chdir(orig_wd); + + panel->remove_menu = NULL; + + icon_path = mb_dot_desktop_icon_get_full_path (panel->theme_name, + 16, HIDE_IMG ); + + mb_menu_add_item_to_menu(panel->mbmenu, panel->mbmenu->rootmenu, _("Hide"), + icon_path, NULL , + panel_menu_hide_cb, (void *)panel, MBMENU_NO_SORT); + + + if (panel->system_tray_id > 0) + mb_menu_add_item_to_menu(panel->mbmenu, panel->mbmenu->rootmenu, + "Exit", + NULL, NULL , + panel_menu_exit_cb, + (void *)panel, MBMENU_NO_SORT); + + + if (icon_path) free(icon_path); + + return; +} + diff --git a/src/panel_menu.h b/src/panel_menu.h new file mode 100644 index 0000000..87839ab --- /dev/null +++ b/src/panel_menu.h @@ -0,0 +1,27 @@ +#ifndef _HAVE_PANEL_MENU_H +#define _HAVE_PANEL_MENU_H + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(text) gettext(text) +#else +# define _(text) (text) +#endif + +#include "panel.h" + +void panel_menu_exec_cb(MBMenuItem *item); + +void panel_menu_exit_cb(MBMenuItem *item); + +void panel_menu_hide_cb(MBMenuItem *item); + +void panel_menu_move_app_cb(MBMenuItem *item); + +void panel_menu_kill_cb(MBMenuItem *item); + +void panel_menu_update_remove_items(MBPanel *panel); + +void panel_menu_init(MBPanel *panel); + +#endif diff --git a/src/panel_util.c b/src/panel_util.c new file mode 100644 index 0000000..593413f --- /dev/null +++ b/src/panel_util.c @@ -0,0 +1,233 @@ +#include "panel_util.h" + +extern MBPanel* G_panel; + +void +util_cleanup_children(int s) +{ + DBG("DIE DIE\n"); + kill(-getpid(), 15); /* kill every one in our process group */ + exit(0); +} + +void +util_install_signal_handlers(void) +{ + + signal (SIGCHLD, SIG_IGN); /* kernel can deal with zombies */ + signal (SIGINT, util_cleanup_children); + signal (SIGQUIT, util_cleanup_children); + signal (SIGTERM, util_cleanup_children); + signal (SIGHUP, util_handle_hup); + signal (SIGALRM, util_handle_alarm); + // signal (SIGSEGV, cleanup_children); + +} + +int +util_handle_xerror(Display *dpy, XErrorEvent *e) +{ + char msg[255]; + XGetErrorText(dpy, e->error_code, msg, sizeof msg); + fprintf(stderr, "Panel X error (%#lx):\n %s (opcode: %i)\n", + e->resourceid, msg, e->request_code); + return 0; +} + +pid_t +util_fork_exec(char *cmd) +{ + pid_t pid, mypid; + mypid = getpid(); + pid = fork(); + + switch (pid) { + case 0: + setpgid (0, mypid); /* set pgid to parents pid */ + mb_exec(cmd); + fprintf(stderr, "exec failed, cleaning up child\n"); + exit(1); + case -1: + fprintf(stderr, "can't fork\n"); break; + } + return pid; +} + + +void +util_handle_alarm(int s) +{ + MBPanel *p = G_panel; + DBG("%s() called", __func__); + session_save(p); +} + +void +util_handle_hup(int s) +{ + MBPanel *p = G_panel; + DBG("%s() called", __func__); + + if (p != NULL) + { + p->reload_pending = True; + } +} + +void +util_get_mouse_position(MBPanel *panel, int *x, int *y) +{ + Window mouse_root, mouse_win; + int win_x, win_y; + unsigned int mask; + + XQueryPointer(panel->dpy, panel->win_root, &mouse_root, &mouse_win, + x, y, &win_x, &win_y, &mask); +} + +pid_t +util_get_window_pid_from_prop(MBPanel *panel, Window win) +{ + Atom type; + int format; + long bytes_after; + unsigned int *data = NULL; + long n_items; + int result; + pid_t pid_result = 0; + + result = XGetWindowProperty (panel->dpy, win, + panel->atoms[ATOM_NET_WM_PID], + 0, 16L, + False, XA_CARDINAL, + &type, &format, &n_items, + &bytes_after, (unsigned char **)&data); + + if (result == Success && n_items) + pid_result = *data; + + if (data) XFree(data); + + return pid_result; +} + +Bool +util_get_command_str_from_win(MBPanel *panel, Window win, char **result) +{ + int i, bytes_needed = 0; + char *p = NULL, *cmd = NULL, **argv_win; + int argc_win; + + if (!XGetCommand(panel->dpy, win, &argv_win, &argc_win)) + return False; + + bytes_needed = strlen(argv_win[0])+2; + + for(i=1;i<argc_win;i++) + { + bytes_needed += strlen(argv_win[i])+2; + for (p = argv_win[i]; *p != '\0'; p++) + if (*p == ' ' || *p == '\t') + bytes_needed++; + } + + *result = malloc(sizeof(char)*bytes_needed); + cmd = *result; + + strcpy(cmd, argv_win[0]); + while(*cmd != '\0') cmd++; + + for(i=1;i<argc_win;i++) + { + p = argv_win[i]; + *cmd++ = ' '; + + if (strpbrk(p, " \t") == NULL) + { + while (*p) *cmd++ = *p++; + } else { + *cmd++ = '\''; + while (*p) + { + if (*p == '\'') + *cmd++ = '\\'; + *cmd++ = *p++; + } + *cmd++ = '\''; + } + } + + *cmd = '\0'; + + XFreeStringList(argv_win); + + return True; +} + +Bool +util_xcol_from_spec(MBPanel *panel, MBColor *col, char *spec) +{ + mb_col_set (col, spec); + return True; +} + +Pixmap +util_get_root_pixmap(MBPanel *panel) +{ + Pixmap root_pxm = None; + + Atom type; + int format; + long bytes_after; + Pixmap *data = NULL; + long n_items; + int result; + + result = XGetWindowProperty (panel->dpy, panel->win_root, + panel->atoms[ATOM_XROOTPMAP_ID], + 0, 16L, + False, XA_PIXMAP, + &type, &format, &n_items, + &bytes_after, (unsigned char **)&data); + + if (result == Success && n_items) + root_pxm = *data; + + if (data) XFree(data); + + panel->root_pixmap_id = root_pxm; + + return root_pxm; +} + +unsigned char * +util_get_utf8_prop(MBPanel *panel, Window win, Atom req_atom) +{ + Atom type; + int format; + long bytes_after; + unsigned char *str = NULL; + long n_items; + int result; + + result = XGetWindowProperty (panel->dpy, win, + req_atom, + 0, 1024L, + False, panel->atoms[ATOM_UTF8_STRING], + &type, &format, &n_items, + &bytes_after, (unsigned char **)&str); + + if (result != Success || str == NULL) + { + if (str) XFree (str); + return NULL; + } + + if (type != panel->atoms[ATOM_UTF8_STRING] || format != 8 || n_items == 0) + { + XFree (str); + return NULL; + } + + return str; +} diff --git a/src/panel_util.h b/src/panel_util.h new file mode 100644 index 0000000..01a59b1 --- /dev/null +++ b/src/panel_util.h @@ -0,0 +1,33 @@ +#ifndef _HAVE_PANEL_UTIL_H +#define _HAVE_PANEL_UTIL_H + +#include "panel.h" + +void util_cleanup_children(int s); + +void util_install_signal_handlers(void); + +int util_handle_xerror(Display *dpy, XErrorEvent *e); + +pid_t util_fork_exec(char *cmd); + +void util_handle_alarm(int s); + +void util_handle_hup(int s); + +void util_get_mouse_position(MBPanel *panel, int *x, int *y); + +pid_t util_get_window_pid_from_prop(MBPanel *panel, Window win); + +Bool util_get_command_str_from_win(MBPanel *panel, Window win, char **result); + +Bool +util_xcol_from_spec(MBPanel *panel, MBColor *col, char *spec); + +Pixmap util_get_root_pixmap(MBPanel *panel); + +unsigned char * +util_get_utf8_prop(MBPanel *panel, Window win, Atom req_atom); + + +#endif diff --git a/src/session.c b/src/session.c new file mode 100644 index 0000000..75c29ae --- /dev/null +++ b/src/session.c @@ -0,0 +1,311 @@ +#include "session.h" + +#define DEFAULT_SESSIONS "mbmenu,minitime" + +void +session_set_defaults(MBPanel *panel, char *defaults) +{ + panel->session_defaults_cur_pos = strdup(defaults); + panel->use_alt_session_defaults = True; +} + +void +session_destroy(MBPanel *panel) +{ + char sessionfile[512] = { 0 }; + + if (!panel->use_session) return; + + if (panel->system_tray_id) + snprintf(sessionfile, 512, "%s/.matchbox/%s.%i", getenv("HOME"), + PANELFILE, panel->system_tray_id ); + else + snprintf(sessionfile, 512, "%s/.matchbox/%s", + getenv("HOME"), PANELFILE ); + + unlink(sessionfile); +} + +void +session_init(MBPanel *panel) +{ + char sessionfile[512] = { 0 }; + struct stat st; + + DBG("%s() called\n", __func__); + + panel->session_preexisting_lock = False; + panel->session_run_first_time = False; + + if (!panel->use_session && !panel->use_alt_session_defaults) return; + + /* + if (panel->use_alt_session_defaults) + { + panel->session_cur_gravity = PAPP_GRAVITY_END; + panel->session_run_first_time = True; + } + */ + + if (getenv("HOME") == NULL) + { + fprintf(stderr, "mbdock: unable to get home directory, is HOME set?\n"); + panel->session_preexisting_lock = False; + return; + } + + snprintf(sessionfile, 512, "%s/.matchbox", getenv("HOME")); + + /* Check if ~/.matchbox exists and create if not */ + if (stat(sessionfile, &st) != 0 /* || !S_ISDIR(st.st_mode) */ + || !panel->use_session) + { + if (panel->use_session) + { + fprintf(stderr, "mbdock: creating %s directory for session files\n", + sessionfile); + mkdir(sessionfile, 0755); + } + panel->session_cur_gravity = PAPP_GRAVITY_END; + panel->session_run_first_time = True; + if (!panel->use_alt_session_defaults) + panel->session_defaults_cur_pos = strdup(DEFAULT_SESSIONS); + } + else + { + /* We have a ~/.matchbox , see if we have a session file + and set defualts if not. + */ + if (panel->system_tray_id) + snprintf(sessionfile, 512, "%s/.matchbox/%s.%i", getenv("HOME"), + PANELFILE, panel->system_tray_id ); + else + snprintf(sessionfile, 512, "%s/.matchbox/%s", + getenv("HOME"), PANELFILE ); + + if ((panel->session_fp = fopen(sessionfile, "r")) == NULL) + { + fprintf(stderr, + "mbpanel: Session file does not exist ( tryed %s )\n", + sessionfile); + if (!panel->use_alt_session_defaults) + { + panel->session_defaults_cur_pos = strdup(DEFAULT_SESSIONS); + } + panel->session_cur_gravity = PAPP_GRAVITY_START; + panel->session_run_first_time = True; + } + else + { + DBG("%s() opened %s\n", __func__, sessionfile); + } + } + + panel->session_preexisting_lock = True; /* we are loading session data */ + panel->session_entry_cur[0] = 0; + + session_preexisting_start_next(panel); + +} + +void session_save(MBPanel *panel) +{ + char *sessionfile = alloca(sizeof(char)*255); + + int i = 0; + MBPanelApp *papp = NULL; + MBPanelApp *papp_heads[] = { panel->apps_start_head, + panel->apps_end_head }; + + DBG("%s() called\n", __func__); + + if (!panel->use_session + || session_preexisting_restarting(panel)) + return; + + if (getenv("HOME") == NULL) + { + fprintf(stderr, "mbdock: unable to get home directory, is HOME set?\n"); + return; + } + + if (panel->system_tray_id) + snprintf(sessionfile, 255, "%s/.matchbox/%s.%i", getenv("HOME"), + PANELFILE, panel->system_tray_id ); + else + snprintf(sessionfile, 255, "%s/.matchbox/%s", + getenv("HOME"), PANELFILE ); + + if ((panel->session_fp = fopen(sessionfile, "w")) == NULL) + { + fprintf(stderr,"mbdock: Unable to create Session file ( %s )\n", + sessionfile); + return; + } + + DBG("%s() still called\n", __func__); + + while (i < 2) + { + papp = papp_heads[i]; + while( papp != NULL ) + { + DBG("%s() writing %s\n", __func__, papp->cmd_str); + if (papp->cmd_str) + { + DBG("%s() writing %s\n", __func__, papp->cmd_str); + fprintf(panel->session_fp, "%s\n", papp->cmd_str); + } + papp = papp->next; + } + fprintf(panel->session_fp, "\t\n" ); + i++; + } + + fclose(panel->session_fp); +} + +Bool +session_preexisting_restarting(MBPanel *panel) +{ + return panel->session_preexisting_lock; +} + +Bool +session_preexisting_start_next(MBPanel *panel) +{ + if (!session_preexisting_restarting(panel)) return False; + + if (panel->session_entry_cur[0] == '\0' + && session_preexisting_get_next(panel)) + { + DBG("%s() starting %s\n", __func__, panel->session_entry_cur); + panel->session_needed_pid = util_fork_exec(panel->session_entry_cur); + return True; + } + return False; +} + +Bool +session_preexisting_win_matches_wanted(MBPanel *panel, Window win, + char *win_cmd) +{ + pid_t win_pid = 0; + if (!session_preexisting_restarting(panel)) return False; + + DBG("%s() called\n", __func__); + + if (panel->session_entry_cur) /* what were waiting on */ + { + /* Check if its got the pid we expect */ + win_pid = util_get_window_pid_from_prop(panel, win); + + DBG("%s() win pid is %i\n", __func__, win_pid); + + if (win_pid && win_pid == panel->session_needed_pid) + return True; + + DBG("%s() pid failed, comparing '%s' vs '%s'\n", __func__, + win_cmd, panel->session_entry_cur ); + + /* check cmd str */ + if (win_cmd && !strncmp(win_cmd, panel->session_entry_cur, + strlen(win_cmd))) + return True; + } + + return False; +} + +void +session_preexisting_clear_current(MBPanel *panel) +{ + panel->session_entry_cur[0] = '\0'; +} + +Bool +session_preexisting_handle_timeouts(MBPanel *panel) +{ + if (!session_preexisting_restarting(panel)) return False; + + DBG("%s() called\n", __func__); + + /* catch session timeouts */ + if (panel->session_entry_cur[0] != '\0') + { + if ( (time(NULL)-panel->session_start_time) > SESSION_TIMEOUT) + { + fprintf(stderr, "Session timeout on %s\n", panel->session_entry_cur); + session_preexisting_clear_current(panel); + session_preexisting_start_next(panel); + } + } + return True; +} + +Bool +session_preexisting_get_next(MBPanel *panel) /* session_restarting_get_next */ +{ + char *tmp; + + if (!session_preexisting_restarting(panel)) return False; + + panel->session_init_offset = 10; + + if (panel->session_run_first_time) + { + if (*panel->session_defaults_cur_pos != '\0') + { + char *prev_pos = panel->session_defaults_cur_pos; + while ( *panel->session_defaults_cur_pos != '\0') + { + if ( *panel->session_defaults_cur_pos == ',') + { + *panel->session_defaults_cur_pos = '\0'; + panel->session_defaults_cur_pos++; + break; + } + panel->session_defaults_cur_pos++; + } + + strncpy(panel->session_entry_cur, prev_pos, 512); + } + else + { + panel->session_run_first_time = False; + panel->session_preexisting_lock = False; + session_save(panel); + return False; + } + } + else + { + + if (fgets(panel->session_entry_cur, 512, panel->session_fp) == NULL) + { + fclose(panel->session_fp); /* All sessions done */ + panel->session_preexisting_lock = False; + session_save(panel); + return False; + } + + /* tab + newline -> change the session gravity */ + if (!strcmp(panel->session_entry_cur, "\t\n")) + { + panel->session_cur_gravity = PAPP_GRAVITY_END; + return session_preexisting_get_next(panel); + } + + if ( panel->session_entry_cur[strlen(panel->session_entry_cur)-1] == '\n') + panel->session_entry_cur[strlen(panel->session_entry_cur)-1] = '\0'; + + if ( (tmp = strstr(panel->session_entry_cur, "\t\t")) != NULL ) + { + panel->session_init_offset = atoi(tmp); + *tmp = '\0'; + } + } + + panel->session_start_time = time(NULL); + return True; +} diff --git a/src/session.h b/src/session.h new file mode 100644 index 0000000..05b527b --- /dev/null +++ b/src/session.h @@ -0,0 +1,37 @@ +#ifndef _HAVE_PANEL_SESSION_H +#define _HAVE_PANEL_SESSION_H + +#include "panel.h" + +#ifdef DEBUG +#define PANELFILE "mbdock.session.debug" +#else +#define PANELFILE "mbdock.session" +#endif + +void +session_destroy(MBPanel *panel); + +void +session_set_defaults(MBPanel *panel, char *defaults); + +void session_init(MBPanel *panel); + +void session_save(MBPanel *panel); + +Bool session_preexisting_restarting(MBPanel *panel); + +Bool session_preexisting_start_next(MBPanel *panel);; + +Bool session_preexisting_win_matches_wanted(MBPanel *panel, Window win, + char *win_cmd); + +void session_preexisting_clear_current(MBPanel *panel); + +Bool session_preexisting_handle_timeouts(MBPanel *panel); + +Bool session_preexisting_get_next(MBPanel *panel); + + + +#endif |