From 3d4a69db156ece98e0bcbe0aecb2436988d4c6b4 Mon Sep 17 00:00:00 2001 From: Daniel Ehlers Date: Tue, 26 Jan 2010 08:45:41 +0100 Subject: [PATCH] first commit --- CHANGELOG | 118 ++++++ COPYING | 22 + CREDITS | 24 ++ INSTALL | 17 + Makefile | 79 ++++ Makefile.common | 39 ++ Makefile.config | 2 + README | 61 +++ bg.c | 207 +++++++++ bg.h | 18 + config.h | 2 + dbg.h | 24 ++ gtkbar.c | 287 +++++++++++++ gtkbar.h | 75 ++++ misc.c | 717 ++++++++++++++++++++++++++++++++ misc.h | 55 +++ panel.c | 875 +++++++++++++++++++++++++++++++++++++++ panel.h | 128 ++++++ plugin.c | 195 +++++++++ plugin.h | 62 +++ systray/Makefile | 23 + systray/egg-marshal.c | 2 + systray/eggmarshalers.c | 164 ++++++++ systray/eggmarshalers.h | 36 ++ systray/eggtraymanager.c | 593 ++++++++++++++++++++++++++ systray/eggtraymanager.h | 88 ++++ systray/fixedtip.c | 158 +++++++ systray/fixedtip.h | 40 ++ systray/main.c | 149 +++++++ 29 files changed, 4260 insertions(+) create mode 100644 CHANGELOG create mode 100644 COPYING create mode 100644 CREDITS create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 Makefile.common create mode 100644 Makefile.config create mode 100644 README create mode 100644 bg.c create mode 100644 bg.h create mode 100644 config.h create mode 100644 dbg.h create mode 100644 gtkbar.c create mode 100644 gtkbar.h create mode 100644 misc.c create mode 100644 misc.h create mode 100644 panel.c create mode 100644 panel.h create mode 100644 plugin.c create mode 100644 plugin.h create mode 100644 systray/Makefile create mode 100644 systray/egg-marshal.c create mode 100644 systray/eggmarshalers.c create mode 100644 systray/eggmarshalers.h create mode 100644 systray/eggtraymanager.c create mode 100644 systray/eggtraymanager.h create mode 100644 systray/fixedtip.c create mode 100644 systray/fixedtip.h create mode 100644 systray/main.c diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..48758f3 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,118 @@ +trayer changelog + +1.0 +* tray code extracted from the main fbpanel sources +* configuration using config file changed into command-line options +* positionx and positiony options removed +* distance option added + +----------------------------------------------------------------------------- +fbpanel changes follow +----------------------------------------------------------------------------- +3.8 +* warnings clean-up +* X11 memory leacher was fixed +* taskbar can be set to show only mapped/iconified and wins from other desktops +* transparency initial support +* gtkbar was ported to gtk2, so fbpanel is compiled with GTK_DISABLE_DEPRECETED +* initial dll support + +3.7 +* rounded corners (optional) +* taskbar view fix + +3.6 +* taskbar icon size fix +* menu icon size fix +* pager checks for drawable pixmap + +3.5 +* Drag-n-Drop for launchbar +* menu plugin +* removed limith for max task size in taskbar + +3.4 +* gtk2.2 linkage fix +* strut fix +* launchbar segfault on wrong config fix +* '&' at the end of action var in launchbar config is depreciated + +3.3 +* taskbar icon size fix + +3.2 +* scroll mouse in pager changes desktops +* packaging and makefiles now are ready for system wide install + additionally ./configure was implemented +* systray checks for another tray already running + +3.1 +* improving icon quility in taskbar +* system tray (aka notification area) support +* NET_WM_STRUT_PARTIAL and NET_WM_STRUT were implmented +* taskbar update icon image on every icon change + +3.0 +* official version bump :-) + +3.0-rc-1 +* porting to GTK2+. port is based on phako's patch + "[ 678749 ] make it compile and work with gtk2" + + +2.2 +* support for XEmbed docklets via gtktray utility + +2.1 +* tray plugin was written +* documentation update +* web site update + +2.0 +* complete engine rewrite +* new plugin API +* pager fixes + +1.4 +* bug-fixes for pager plugin + +1.3 +* middle-click in taskbar will toggle shaded state of a window +* added image plugin - this is simple plugin that just shows an image +* pager eye-candy fixes +* close_module function update + +1.2 +* we've got new module - pager! Yeeaa-Haa!! +* segfault on wrong config file was fixed + +1.1 +* parsing engine was rewritten +* modules' static variables were converted to mallocs +* configurable size and postion of a panel +* ability to specify what modules to load +* '~' is accepted in config files +* + + +1.0 +* 1.0-rc2 was released as 1.0 + +1.0-rc2 +* taskbar config file was added an option to switch tooltips on/off +* added tooltips to taskbar (thanks to Joe MacDonald joe@deserted.net) + +1.0-rc1 +* copyright comments were changed + +1.0-rc0 +* added _NET_WM_STRUT support +* panel now is unfocusable. this fixes iconify bug under sawfish +* panel's height is calculated at run-time, instead of fixed 22 + +0.11 +* improved EWMH/NETWM support +* added openbox support +* added clock customization (thanks to Tooar tooar@gmx.net) +* README was rewrited +* bug fixes diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..f855fa2 --- /dev/null +++ b/COPYING @@ -0,0 +1,22 @@ +Copyright (C) 2005 Maciej "harnir" Delmanowski +Copyright (C) 2002 Anatoly Asviyan (aka Arsen) +Copyright (C) 2000 Peter Zelezny + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Soft- +ware"), to deal in the Software without restriction, including without +limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to +whom the Software is furnished to do so, subject to the following condi- +tions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- +ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..4a6c07a --- /dev/null +++ b/CREDITS @@ -0,0 +1,24 @@ +Thanks goes to: +- Grzegorz Niewęgłowski - code extraction from + fbpanel +- Rafal Bisingier - conversion of configuration using + ~/.fbpanel/* files to commandline options +- Thomas Rydzynski - added new option 'distance' + +fbpanel credits follow +----------------------------------------------------------------------------- +I'd like to thank to GNOME project. I learn a lot from its code +and some pieces were used in fbpanel. Correct for version 3.0, +systray code and wm icon code were copied from GNOME + +Credits to all people who contributed by writing code and solving +bugs. + Joe MacDonald + Jens Georg + and others + +Credits and lot of thanks to Peter Zelezny +an author of fspanel (see http://www.chatjunkies.org/fspanel/ + or http://freshmeat.net/projects/fspanel/) +The first version of fBpanel was started as hacking around fSpanel :-) + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9bcc461 --- /dev/null +++ b/INSTALL @@ -0,0 +1,17 @@ +Installation: + +1. Default way +Most users (99.99%) should use this way :-) + + ./confugire + make + su - + make install + +2. Litle customization +Run ./configure --help to see supported options, then goto step 1 + + + + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d69c5cb --- /dev/null +++ b/Makefile @@ -0,0 +1,79 @@ +# Part 0 +# load common stuff +TOPDIR = . +include $(TOPDIR)/Makefile.common + +# Part 1 +# recursive make +.PHONY: subdirs +all clean distclean install uninstall: subdirs + +SUBDIRS = systray +.PHONY: $(SUBDIRS) +subdirs: $(SUBDIRS) +$(SUBDIRS): + $(MAKE) -C $@ $(MAKECMDGOALS) + + + +SRC = panel.c misc.c plugin.c gtkbar.c bg.c +OBJ = $(SRC:%.c=%.o) +DEP = $(SRC:%.c=%.dep) + +SYSTRAYOBJ = systray/systray.o +SYSTRAYOBJ: systray + + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),distclean) +ifneq ($(MAKECMDGOALS),tar) +-include $(DEP) +endif +endif +endif + + +TARGET = trayer +$(TARGET): $(OBJ) $(SYSTRAYOBJ) + $(CC) $(LDFLAGS) $(LIBS) $(OBJ) $(SYSTRAYOBJ) -o $@ +ifeq (,$(DEVEL)) + strip $@ +endif + +all: $(TARGET) + + +clean: + $(RM) $(TARGET) $(OBJ) $(DEP) *~ + +distclean: + rm -f Makefile.config config.h + +install: + install -d $(PREFIX)/bin + install -m 755 $(TARGET) $(PREFIX)/bin + +uninstall: + rm -f $(PREFIX)/bin/$(TARGET) + +.PHONY: tar + + +CWD=$(shell pwd) +VER=$(shell grep -e "\#define[[:space:]]\+VERSION[[:space:]]\+" panel.c | \ + sed -e 's/^[^\"]\+\"//' -e 's/\".*$$//' ) + + +tar: + $(MAKE) distclean + cd ..; \ + if [ -e trayer-$(VER) ]; then \ + echo trayer-$(VER) already exist; \ + echo "won't override";\ + exit 1;\ + else\ + ln -s $(CWD) trayer-$(VER);\ + tar --exclude CVS -hzcvf trayer-$(VER).tgz trayer-$(VER);\ + rm -f trayer-$(VER);\ + fi; + diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..b4bebb2 --- /dev/null +++ b/Makefile.common @@ -0,0 +1,39 @@ +ifeq (,$(TOPDIR)) +$(error TOPDIR variable must be defined) +endif + +all: + +$(TOPDIR)/Makefile.config: + $(error Please run $(TOPDIR)/configure first) + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),distclean) +ifneq ($(MAKECMDGOALS),tar) +-include $(TOPDIR)/Makefile.config +endif +endif +endif + +CC = gcc +LIBS = $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu +INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) +CFLAGS = -O2 # overwriten by command line or env. variable +CFLAGS += -Wall # always nice to have +ifneq (,$(DEVEL)) +CFLAGS := -g -Wall +endif + +# -DGTK_DISABLE_DEPRECATED does not work yet +CFLAGS += -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED + +%.o: %.c + $(CC) $(CFLAGS) $(INCS) -c $< + +%.dep: %.c + $(CC) $(CFLAGS) $(INCS) -MM $< -o $@ + +.PHONY: all clean distclean install uninstall + +distclean: clean +install: all diff --git a/Makefile.config b/Makefile.config new file mode 100644 index 0000000..0826f64 --- /dev/null +++ b/Makefile.config @@ -0,0 +1,2 @@ +PREFIX:=/usr +DEVEL:= diff --git a/README b/README new file mode 100644 index 0000000..20999ce --- /dev/null +++ b/README @@ -0,0 +1,61 @@ + trayer + +NAME + trayer is a lightweight GTK2-based systray for UNIX desktop + +SYNOPSYS + trayer [OPTION]... + +DESCRIPTION + trayer is small program designed to provide systray functionality present + in GNOME/KDE desktop enviroments for window managers wchich doesn't + support that function. It's similar to other applications such as + 'peksystray' and 'docker'. + + trayer code was extracted from fbpanel application, you can find more + about it on it's homepage: http://fbpanel.sourceforge.net/ + + You can find new versions of trayer and support on FVWM-Crystal + project homepage: http://fvwm-crystal.berlios.de/ + +OPTIONS + -h - prints help message and exits + -v - prints version and exits + --edge - screen edge to use + --align - alignment + --margin - length of margin in pixels + --distance - space between trayer's window and screen + edge + --widthtype - how panel width is calculated: + request - follow widgets' size requests. can shrink + or grow dynamically + pixel - ocupy fixed number of pixels, then 'width' + variable holds a number + percent - be 'width' precent of an edge + --width - width of a panel (not used with --widthtype=request) + --heighttype - how panel height is calcilated: + pixel - ocupy fixed number of pixels, then 'height' + variable holds a number + --height - height of a panel in pixels + --SetDockTpe - Identify panel window type as dock + --SetPartialStrut - Reserve panel's space so that it will not be covered + by maximazied windows + --transparent - use transparency + --tint - color used to "tint" background wallpaper + with + --alpha - pocentage of transparency <0-256> + --expand - specifies if trayer can accomodate extra + space or not + --padding - extra space between trayer's window frame + and docked icons + --monitor - define the mointor on which you like + trayer to appear, number of zero to number + of monitors minus one + +AUTHORS + Maciej Delmanowski + Anatoly Asviyan - fbpanel + Daniel Ehlers - monitor + + + diff --git a/bg.c b/bg.c new file mode 100644 index 0000000..735a09b --- /dev/null +++ b/bg.c @@ -0,0 +1,207 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bg.h" +#include "dbg.h" +#include "config.h" + +static Display *dpy; +//static int screen; +static Window xroot; +static Pixmap xrootbg = 0xc046ad; +static GC gc; +static Atom id = 0; + +static Pixmap +bg_get_root_bg() +{ + + Pixmap ret; + + ENTER; + ret = None; + if (id) { + int act_format, c = 2 ; + u_long nitems ; + u_long bytes_after ; + u_char *prop ; + Atom dummy_id; + + do { + if (XGetWindowProperty(dpy, xroot, id, 0, 1, + False, XA_PIXMAP, &dummy_id, &act_format, + &nitems, &bytes_after, &prop) == Success) { + if (prop) { + ret = *((Pixmap *)prop); + XFree(prop); + break; + } + } + } while (--c > 0); + } + //fprintf(stderr, "_XROOTPMAP_ID = 0x%x\n", ret); + RET(ret); +} + +GdkPixmap * +bg_new_for_win(Window win) +{ + Window dummy; + Pixmap bgpix; + GdkPixmap *gbgpix; + int width, height ; + int border, depth, x, y ; + + ENTER; + XGetGeometry(dpy, win, &dummy, &x, &y, &width, &height, &border, &depth); + XTranslateCoordinates(dpy, win, xroot, 0, 0, &x, &y, &dummy); + gbgpix = gdk_pixmap_new(NULL, width, height, depth); + if (!gbgpix) + RET(NULL); + bgpix = gdk_x11_drawable_get_xid(gbgpix); + XSetTSOrigin(dpy, gc, -x, -y) ; + XFillRectangle(dpy, bgpix, gc, 0, 0, width, height); + RET(gbgpix); +} + +void +bg_init(Display *dpyn) +{ + XGCValues gcv; + uint mask; + + ENTER; + dpy = dpyn; + id = XInternAtom(dpy, "_XROOTPMAP_ID", False); + xroot = DefaultRootWindow(dpy); + xrootbg = bg_get_root_bg(); + + gcv.ts_x_origin = 0; + gcv.ts_y_origin = 0; + gcv.fill_style = FillTiled; + mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle; + if (xrootbg) { + gcv.tile = xrootbg; + mask |= GCTile ; + } + gc = XCreateGC (dpy, xroot, mask, &gcv) ; + RET(); +} + +void +bg_rootbg_changed() +{ + ENTER; + xrootbg = bg_get_root_bg(); + if (xrootbg != None) { + XGCValues gcv; + + gcv.tile = xrootbg; + XChangeGC(dpy, gc, GCTile, &gcv); + DBG("changed\n"); + } + RET(); +} + +void +bg_close() +{ + ENTER; + XFreeGC(dpy, gc); + RET(); +} + +void +modify_drawable(GdkDrawable *base, GdkGC *gc, guint32 tintcolor, gint alpha) +{ + int w, h; + GdkPixbuf *ret, *ret2; + static GdkColormap *cmap = NULL; + + ENTER; + gdk_drawable_get_size (base, &w, &h); + if (!cmap) { + cmap = gdk_colormap_get_system (); + } + ret = gdk_pixbuf_get_from_drawable (NULL, base, cmap, 0, 0, 0, 0, w, h); + if (!ret) + RET(); + ret2 = gdk_pixbuf_composite_color_simple(ret, w, h, + GDK_INTERP_HYPER, alpha, MIN(w, h), tintcolor, tintcolor); + + //gdk_pixbuf_render_to_drawable (ret2, base, gc, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0); + gdk_draw_pixbuf (base, gc, ret2, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref(ret); + g_object_unref(ret2); + RET(); +} + +#ifdef TRANSPARENCY + +#include +//#include +#include /* For gdk_rectangle_union() */ +#include +#include +#include +#include + + + +typedef struct _GdkWindowPaint GdkWindowPaint; + +struct _GdkWindowPaint +{ + GdkRegion *region; + GdkPixmap *pixmap; + gint x_offset; + gint y_offset; +}; + + + +GdkGC * +gdk_window_get_bg_gc (GdkWindow *window, + GdkWindowPaint *paint) +{ + GdkWindowObject *private = (GdkWindowObject *)window; + guint gc_mask = 0; + GdkGCValues gc_values; + + ENTER; + if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent) { + GdkWindowPaint tmp_paint = *paint; + tmp_paint.x_offset += private->x; + tmp_paint.y_offset += private->y; + + return gdk_window_get_bg_gc (GDK_WINDOW (private->parent), &tmp_paint); + } else if (private->bg_pixmap && + private->bg_pixmap != GDK_PARENT_RELATIVE_BG && + private->bg_pixmap != GDK_NO_BG) { + gc_values.fill = GDK_TILED; + gc_values.tile = private->bg_pixmap; + gc_values.ts_x_origin = -paint->x_offset; + gc_values.ts_y_origin = -paint->y_offset; + gc_mask = GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN; + + DBG("x_offset=%d y_offset=%d\n", paint->x_offset, paint->y_offset); + return gdk_gc_new_with_values (paint->pixmap, &gc_values, gc_mask); + } else { + GdkGC *gc = _gdk_drawable_get_scratch_gc (paint->pixmap, FALSE); + + gdk_gc_set_foreground (gc, &(private->bg_color)); + + return g_object_ref (gc); + } +} + +#endif diff --git a/bg.h b/bg.h new file mode 100644 index 0000000..749e686 --- /dev/null +++ b/bg.h @@ -0,0 +1,18 @@ +#ifndef _BG_H_ +#define _BG_H_ + +#include + +#include +#include +#include +#include +#include + +void bg_init(); +void bg_rootbg_changed(); +GdkPixmap *bg_new_for_win(Window win); +void modify_drawable(GdkDrawable *base, GdkGC *gc, guint32 tintcolor, gint alpha); + +#endif + diff --git a/config.h b/config.h new file mode 100644 index 0000000..22d6e9e --- /dev/null +++ b/config.h @@ -0,0 +1,2 @@ +//created by ./configure script +#define PREFIX "/usr" diff --git a/dbg.h b/dbg.h new file mode 100644 index 0000000..dd982e6 --- /dev/null +++ b/dbg.h @@ -0,0 +1,24 @@ +#include + +#define ERR(fmt, args...) fprintf(stderr, fmt, ## args) +#define DBG2(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args) +#define ENTER2 do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0) +#define RET2(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\ +return args; } while(0) + +#ifdef DBG + +#define ENTER do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0) +#define RET(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\ +return args; } while(0) +#define DBG(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args) + +#else + + +#define ENTER do { } while(0) +#define RET(args...) return args; +#define DBG(fmt, args...) do { } while(0) + +#endif + diff --git a/gtkbar.c b/gtkbar.c new file mode 100644 index 0000000..fbaac6f --- /dev/null +++ b/gtkbar.c @@ -0,0 +1,287 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "gtkbar.h" + +#include "dbg.h" + +#define MAX_CHILD_SIZE 150 + +static void gtk_bar_class_init (GtkBarClass *klass); +static void gtk_bar_init (GtkBar *box); +static void gtk_bar_size_request (GtkWidget *widget, GtkRequisition *requisition); +static void gtk_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation); +//static gint gtk_bar_expose (GtkWidget *widget, GdkEventExpose *event); + +static GtkBoxClass *parent_class = NULL; + +GType +gtk_bar_get_type (void) +{ + static GType bar_type = 0; + + if (!bar_type) + { + static const GTypeInfo bar_info = + { + sizeof (GtkBarClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_bar_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkBar), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_bar_init, + }; + + bar_type = g_type_register_static (GTK_TYPE_BOX, "GtkBar", + &bar_info, 0); + } + + return bar_type; +} + +static void +gtk_bar_class_init (GtkBarClass *class) +{ + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + widget_class = (GtkWidgetClass*) class; + + widget_class->size_request = gtk_bar_size_request; + widget_class->size_allocate = gtk_bar_size_allocate; + //widget_class->expose_event = gtk_bar_expose; + +} + +static void +gtk_bar_init (GtkBar *bar) +{ + bar->max_child_size = MAX_CHILD_SIZE; + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (bar), TRUE); +} + +GtkWidget* +gtk_bar_new (GtkBarOrientation orient, gint spacing) +{ + GtkBar *bar; + + bar = g_object_new (GTK_TYPE_BAR, NULL); + GTK_BOX (bar)->spacing = spacing; + bar->orient = orient; + return (GtkWidget *)bar; +} + + +static void +gtk_bar_size_request (GtkWidget *widget, GtkRequisition *requisition) +{ + GtkBox *box; + GtkBar *bar; + GtkBoxChild *child; + GList *children; + gint nvis_children; + + box = GTK_BOX (widget); + bar = GTK_BAR (widget); + requisition->width = 0; + requisition->height = 0; + nvis_children = 0; + + children = box->children; + while (children) { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) { + GtkRequisition child_requisition; + + gtk_widget_size_request (child->widget, &child_requisition); + if (bar->orient == GTK_BAR_HORIZ) + requisition->height = MAX (requisition->height, child_requisition.height); + else + requisition->width = MAX (requisition->width, child_requisition.width); + nvis_children++; + } + } + + if (nvis_children > 0) { + if (bar->orient == GTK_BAR_HORIZ) { + requisition->width = nvis_children * GTK_BAR(widget)->max_child_size; + requisition->width += (nvis_children - 1) * box->spacing; + } else { + requisition->height = nvis_children * GTK_BAR(widget)->max_child_size; + requisition->height += (nvis_children - 1) * box->spacing; + } + } + + requisition->width += GTK_CONTAINER (box)->border_width * 2; + requisition->height += GTK_CONTAINER (box)->border_width * 2; +} + +static void +gtk_bar_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkBox *box; + GtkBar *bar; + GtkBoxChild *child; + GList *children; + GtkAllocation child_allocation; + gint nvis_children; + gint bw; + gint tmp; + + box = GTK_BOX (widget); + bar = GTK_BAR (widget); + widget->allocation = *allocation; + + bw = GTK_CONTAINER (box)->border_width; + nvis_children = 0; + children = box->children; + while (children) { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + nvis_children += 1; + } + + if (nvis_children == 0) + return; + + child_allocation.x = allocation->x + bw; + child_allocation.y = allocation->y + bw; + + if (bar->orient == GTK_BAR_HORIZ) { + child_allocation.height = MAX (1, (gint) allocation->height - bw * 2); + tmp = (allocation->width - bw * 2 - (nvis_children - 1) * box->spacing); + child_allocation.width = MAX (1, MIN(tmp / nvis_children, GTK_BAR(widget)->max_child_size)); + } else { + child_allocation.width = MAX (1, (gint) allocation->width - bw * 2); + tmp = (allocation->height - bw * 2 - (nvis_children - 1) * box->spacing); + child_allocation.height = MAX (1, MIN(tmp / nvis_children, GTK_BAR(widget)->max_child_size)); + } + + children = box->children; + while (children) { + child = children->data; + children = children->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) { + gtk_widget_size_allocate (child->widget, &child_allocation); + if (bar->orient == GTK_BAR_HORIZ) + child_allocation.x += child_allocation.width + box->spacing; + else + child_allocation.y += child_allocation.height + box->spacing; + } + } +} + + +void +gtk_bar_set_max_child_size(GtkBar *bar, gint size) +{ + g_return_if_fail (GTK_IS_BAR (bar)); + + if (size != bar->max_child_size) { + bar->max_child_size = size; + //g_object_notify (G_OBJECT (bar), "spacing"); + gtk_widget_queue_resize (GTK_WIDGET (bar)); + } +} + + +#if 0 + +static void +gtk_bar_paint (GtkWidget *widget, GdkRectangle *area) +{ + ENTER2; + gdk_window_clear(widget->window); + RET2(); + if (!GTK_WIDGET_APP_PAINTABLE (widget)) + gtk_paint_flat_box (widget->style, widget->window, + widget->state, GTK_SHADOW_NONE, + area, widget, NULL, + 0, 0, 20001, 20001); + RET2(); +} + +static gint +gtk_bar_expose (GtkWidget *widget, GdkEventExpose *event) +{ + ENTER; + RET(FALSE); + /* + DBG("pos=(%d,%d) dim=(%d,%d)\n", event->area.x, event->area.y, event->area.width, event->area.height); + if (GTK_WIDGET_DRAWABLE (widget)) { + DBG("is drawable\n"); + //if (!GTK_WIDGET_NO_WINDOW (widget)) + gtk_bar_paint (widget, &event->area); + + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + + } + RET(FALSE); + */ +} +/* +void +gtk_container_propagate_expose (GtkContainer *container, + GtkWidget *child, + GdkEventExpose *event) +{ + GdkEvent *child_event; + + ENTER; + g_return_if_fail (GTK_IS_CONTAINER (container)); + g_return_if_fail (GTK_IS_WIDGET (child)); + g_return_if_fail (event != NULL); + + g_assert (child->parent == GTK_WIDGET (container)); + + if (GTK_WIDGET_DRAWABLE (child) && + GTK_WIDGET_NO_WINDOW (child) && + (child->window == event->window)) + { + child_event = gdk_event_new (GDK_EXPOSE); + child_event->expose = *event; + g_object_ref (child_event->expose.window); + + child_event->expose.region = gtk_widget_region_intersect (child, event->region); + if (!gdk_region_empty (child_event->expose.region)) + { + gdk_region_get_clipbox (child_event->expose.region, &child_event->expose.area); + gtk_widget_send_expose (child, child_event); + } + gdk_event_free (child_event); + } +} +*/ +#endif diff --git a/gtkbar.h b/gtkbar.h new file mode 100644 index 0000000..b10c577 --- /dev/null +++ b/gtkbar.h @@ -0,0 +1,75 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GTK_BAR_H__ +#define __GTK_BAR_H__ + + +#include +#include + + +#ifdef __cplusplus +//extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_BAR (gtk_bar_get_type ()) +#define GTK_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_BAR, GtkBar)) +#define GTK_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_BAR, GtkBarClass)) +#define GTK_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_BAR)) +#define GTK_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_BAR)) +#define GTK_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_BAR, GtkBarClass)) + + +typedef struct _GtkBar GtkBar; +typedef struct _GtkBarClass GtkBarClass; +typedef enum { GTK_BAR_VERTICAL, GTK_BAR_HORIZ } GtkBarOrientation; + +struct _GtkBar +{ + GtkBox box; + gint max_child_size; + GtkBarOrientation orient; +}; + +struct _GtkBarClass +{ + GtkBoxClass parent_class; +}; + + +GType gtk_bar_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_bar_new (GtkBarOrientation orient, gint spacing); +void gtk_bar_set_max_child_size(GtkBar *bar, gint size); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_BAR_H__ */ diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..224ba06 --- /dev/null +++ b/misc.c @@ -0,0 +1,717 @@ + + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "misc.h" +#include "panel.h" + +//#define DEBUG +#include "dbg.h" + + +/* X11 data types */ +Atom a_UTF8_STRING; +Atom a_XROOTPMAP_ID; + +/* old WM spec */ +Atom a_WM_STATE; +Atom a_WM_CLASS; + +/* new NET spec */ +Atom a_NET_WORKAREA; +Atom a_NET_CLIENT_LIST; +Atom a_NET_CLIENT_LIST_STACKING; +Atom a_NET_NUMBER_OF_DESKTOPS; +Atom a_NET_CURRENT_DESKTOP; +Atom a_NET_DESKTOP_NAMES; +Atom a_NET_ACTIVE_WINDOW; +Atom a_NET_WM_DESKTOP; +Atom a_NET_WM_STATE; +Atom a_NET_WM_STATE_SKIP_TASKBAR; +Atom a_NET_WM_STATE_SKIP_PAGER; +Atom a_NET_WM_STATE_STICKY; +Atom a_NET_WM_STATE_HIDDEN; +Atom a_NET_WM_STATE_SHADED; +Atom a_NET_WM_WINDOW_TYPE; +Atom a_NET_WM_WINDOW_TYPE_DESKTOP; +Atom a_NET_WM_WINDOW_TYPE_DOCK; +Atom a_NET_WM_WINDOW_TYPE_TOOLBAR; +Atom a_NET_WM_WINDOW_TYPE_MENU; +Atom a_NET_WM_WINDOW_TYPE_UTILITY; +Atom a_NET_WM_WINDOW_TYPE_SPLASH; +Atom a_NET_WM_WINDOW_TYPE_DIALOG; +Atom a_NET_WM_WINDOW_TYPE_NORMAL; +Atom a_NET_WM_DESKTOP; +Atom a_NET_WM_NAME; +Atom a_NET_WM_STRUT; +Atom a_NET_WM_STRUT_PARTIAL; +Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR; + +pair allign_pair[] = { + { ALLIGN_NONE, "none" }, + { ALLIGN_LEFT, "left" }, + { ALLIGN_RIGHT, "right" }, + { ALLIGN_CENTER, "center"}, + { 0, NULL }, +}; + +pair edge_pair[] = { + { EDGE_NONE, "none" }, + { EDGE_LEFT, "left" }, + { EDGE_RIGHT, "right" }, + { EDGE_TOP, "top" }, + { EDGE_BOTTOM, "bottom" }, + { 0, NULL }, +}; + +pair width_pair[] = { + { WIDTH_NONE, "none" }, + { WIDTH_REQUEST, "request" }, + { WIDTH_PIXEL, "pixel" }, + { WIDTH_PERCENT, "percent" }, + { 0, NULL }, +}; + +pair height_pair[] = { + { HEIGHT_NONE, "none" }, + { HEIGHT_PIXEL, "pixel" }, + { 0, NULL }, +}; + +pair bool_pair[] = { + { 0, "false" }, + { 1, "true" }, + { 0, NULL }, +}; +pair pos_pair[] = { + { POS_NONE, "none" }, + { POS_START, "start" }, + { POS_END, "end" }, + { 0, NULL}, +}; + + +int +str2num(pair *p, gchar *str, int defval) +{ + ENTER; + for (;p && p->str; p++) { + if (!g_ascii_strcasecmp(str, p->str)) + RET(p->num); + } + RET(defval); +} + +gchar * +num2str(pair *p, int num, gchar *defval) +{ + ENTER; + for (;p && p->str; p++) { + if (num == p->num) + RET(p->str); + } + RET(defval); +} + +int +get_line(FILE *fp, line *s) +{ + gchar *tmp, *tmp2; + + ENTER; + if (!fp) { + s->type = LINE_NONE; + RET(s->type); + } + s->type = LINE_NONE; + while (fgets(s->str, s->len, fp)) { + g_strstrip(s->str); + + if (s->str[0] == '#' || s->str[0] == 0) { + continue; + } + DBG( ">> %s\n", s->str); + if (!g_ascii_strcasecmp(s->str, "}")) { + s->type = LINE_BLOCK_END; + break; + } + + s->t[0] = s->str; + for (tmp = s->str; isalnum(*tmp); tmp++); + for (tmp2 = tmp; isspace(*tmp2); tmp2++); + if (*tmp2 == '=') { + for (++tmp2; isspace(*tmp2); tmp2++); + s->t[1] = tmp2; + *tmp = 0; + s->type = LINE_VAR; + } else if (*tmp2 == '{') { + *tmp = 0; + s->type = LINE_BLOCK_START; + } else { + ERR( "parser: unknown token: '%c'\n", *tmp2); + } + break; + } + RET(s->type); + +} + +int +get_line_as_is(FILE *fp, line *s) +{ + gchar *tmp, *tmp2; + + ENTER; + if (!fp) { + s->type = LINE_NONE; + RET(s->type); + } + s->type = LINE_NONE; + while (fgets(s->str, s->len, fp)) { + g_strstrip(s->str); + if (s->str[0] == '#' || s->str[0] == 0) + continue; + DBG( ">> %s\n", s->str); + if (!g_ascii_strcasecmp(s->str, "}")) { + s->type = LINE_BLOCK_END; + DBG( " : line_block_end\n"); + break; + } + for (tmp = s->str; isalnum(*tmp); tmp++); + for (tmp2 = tmp; isspace(*tmp2); tmp2++); + if (*tmp2 == '=') { + s->type = LINE_VAR; + } else if (*tmp2 == '{') { + s->type = LINE_BLOCK_START; + } else { + DBG( " : ? <%c>\n", *tmp2); + } + break; + } + RET(s->type); + +} + +void resolve_atoms() +{ + ENTER; + + a_UTF8_STRING = XInternAtom(GDK_DISPLAY(), "UTF8_STRING", False); + a_XROOTPMAP_ID = XInternAtom(GDK_DISPLAY(), "_XROOTPMAP_ID", False); + a_WM_STATE = XInternAtom(GDK_DISPLAY(), "WM_STATE", False); + a_WM_CLASS = XInternAtom(GDK_DISPLAY(), "WM_CLASS", False); + a_NET_WORKAREA = XInternAtom(GDK_DISPLAY(), "_NET_WORKAREA", False); + a_NET_CLIENT_LIST = XInternAtom(GDK_DISPLAY(), "_NET_CLIENT_LIST", False); + a_NET_CLIENT_LIST_STACKING = XInternAtom(GDK_DISPLAY(), "_NET_CLIENT_LIST_STACKING", False); + a_NET_NUMBER_OF_DESKTOPS = XInternAtom(GDK_DISPLAY(), "_NET_NUMBER_OF_DESKTOPS", False); + a_NET_CURRENT_DESKTOP = XInternAtom(GDK_DISPLAY(), "_NET_CURRENT_DESKTOP", False); + a_NET_DESKTOP_NAMES = XInternAtom(GDK_DISPLAY(), "_NET_DESKTOP_NAMES", False); + a_NET_ACTIVE_WINDOW = XInternAtom(GDK_DISPLAY(), "_NET_ACTIVE_WINDOW", False); + a_NET_WM_DESKTOP = XInternAtom(GDK_DISPLAY(), "_NET_WM_DESKTOP", False); + a_NET_WM_STATE = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE", False); + a_NET_WM_STATE_SKIP_TASKBAR = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SKIP_TASKBAR", False); + a_NET_WM_STATE_SKIP_PAGER = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SKIP_PAGER", False); + a_NET_WM_STATE_STICKY = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_STICKY", False); + a_NET_WM_STATE_HIDDEN = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_HIDDEN", False); + a_NET_WM_STATE_SHADED = XInternAtom(GDK_DISPLAY(), "_NET_WM_STATE_SHADED", False); + a_NET_WM_WINDOW_TYPE = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE", False); + + a_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DESKTOP", False); + a_NET_WM_WINDOW_TYPE_DOCK = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DOCK", False); + a_NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_TOOLBAR", False); + a_NET_WM_WINDOW_TYPE_MENU = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_MENU", False); + a_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_UTILITY", False); + a_NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_SPLASH", False); + a_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_DIALOG", False); + a_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(GDK_DISPLAY(), "_NET_WM_WINDOW_TYPE_NORMAL", False); + a_NET_WM_DESKTOP = XInternAtom(GDK_DISPLAY(), "_NET_WM_DESKTOP", False); + a_NET_WM_NAME = XInternAtom(GDK_DISPLAY(), "_NET_WM_NAME", False); + a_NET_WM_STRUT = XInternAtom(GDK_DISPLAY(), "_NET_WM_STRUT", False); + a_NET_WM_STRUT_PARTIAL = XInternAtom(GDK_DISPLAY(), "_NET_WM_STRUT_PARTIAL", False); + a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR + = XInternAtom(GDK_DISPLAY(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); + + RET(); +} + + +void +Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4) +{ + XClientMessageEvent xev; + + xev.type = ClientMessage; + xev.window = win; + xev.message_type = type; + xev.format = 32; + xev.data.l[0] = l0; + xev.data.l[1] = l1; + xev.data.l[2] = l2; + xev.data.l[3] = l3; + xev.data.l[4] = l4; + XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False, + (SubstructureNotifyMask | SubstructureRedirectMask), + (XEvent *) & xev); +} + +void * +get_xaproperty (Window win, Atom prop, Atom type, int *nitems) +{ + Atom type_ret; + int format_ret; + unsigned long items_ret; + unsigned long after_ret; + unsigned char *prop_data; + + ENTER; + prop_data = NULL; + XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False, + type, &type_ret, &format_ret, &items_ret, + &after_ret, &prop_data); + if (nitems) + *nitems = items_ret; + RET(prop_data); +} + +static char* +text_property_to_utf8 (const XTextProperty *prop) +{ + char **list; + int count; + char *retval; + + ENTER; + list = NULL; + count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding), + prop->format, + prop->value, + prop->nitems, + &list); + + DBG("count=%d\n", count); + if (count == 0) + return NULL; + + retval = list[0]; + list[0] = g_strdup (""); /* something to free */ + + g_strfreev (list); + + RET(retval); +} + +char * +get_textproperty(Window win, Atom atom) +{ + XTextProperty text_prop; + char *retval; + + ENTER; + if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) { + DBG("format=%d enc=%d nitems=%d value=%s \n", + text_prop.format, + text_prop.encoding, + text_prop.nitems, + text_prop.value); + retval = text_property_to_utf8 (&text_prop); + if (text_prop.nitems > 0) + XFree (text_prop.value); + RET(retval); + + } + RET(NULL); +} + + +int +get_net_number_of_desktops() +{ + int desknum; + unsigned long *data; + + ENTER; + data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS, + XA_CARDINAL, 0); + if (!data) + RET(0); + + desknum = *data; + XFree (data); + RET(desknum); +} + + +int +get_net_current_desktop () +{ + int desk; + unsigned long *data; + + ENTER; + data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0); + if (!data) + RET(0); + + desk = *data; + XFree (data); + RET(desk); +} + +int +get_net_wm_desktop(Window win) +{ + int desk = 0; + unsigned long *data; + + ENTER; + data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0); + if (data) { + desk = *data; + XFree (data); + } + RET(desk); +} + +void +get_net_wm_state(Window win, net_wm_state *nws) +{ + Atom *state; + int num3; + + + ENTER; + bzero(nws, sizeof(nws)); + if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3))) + RET(); + + DBG( "%x: netwm state = { ", (unsigned int)win); + while (--num3 >= 0) { + if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) { + DBG("NET_WM_STATE_SKIP_PAGER "); + nws->skip_pager = 1; + } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) { + DBG( "NET_WM_STATE_SKIP_TASKBAR "); + nws->skip_taskbar = 1; + } else if (state[num3] == a_NET_WM_STATE_STICKY) { + DBG( "NET_WM_STATE_STICKY "); + nws->sticky = 1; + } else if (state[num3] == a_NET_WM_STATE_HIDDEN) { + DBG( "NET_WM_STATE_HIDDEN "); + nws->hidden = 1; + } else if (state[num3] == a_NET_WM_STATE_SHADED) { + DBG( "NET_WM_STATE_SHADED "); + nws->shaded = 1; + } else { + DBG( "... "); + } + } + XFree(state); + DBG( "}\n"); + RET(); +} + + + + +void +get_net_wm_window_type(Window win, net_wm_window_type *nwwt) +{ + Atom *state; + int num3; + + + ENTER; + bzero(nwwt, sizeof(nwwt)); + if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3))) + RET(); + + DBG( "%x: netwm state = { ", (unsigned int)win); + while (--num3 >= 0) { + if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) { + DBG("NET_WM_WINDOW_TYPE_DESKTOP "); + nwwt->desktop = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) { + DBG( "NET_WM_WINDOW_TYPE_DOCK "); + nwwt->dock = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) { + DBG( "NET_WM_WINDOW_TYPE_TOOLBAR "); + nwwt->toolbar = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) { + DBG( "NET_WM_WINDOW_TYPE_MENU "); + nwwt->menu = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) { + DBG( "NET_WM_WINDOW_TYPE_UTILITY "); + nwwt->utility = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) { + DBG( "NET_WM_WINDOW_TYPE_SPLASH "); + nwwt->splash = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) { + DBG( "NET_WM_WINDOW_TYPE_DIALOG "); + nwwt->dialog = 1; + } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) { + DBG( "NET_WM_WINDOW_TYPE_NORMAL "); + nwwt->normal = 1; + } else { + DBG( "... "); + } + } + XFree(state); + DBG( "}\n"); + RET(); +} + + + + + +int +get_wm_state (Window win) +{ + unsigned long *data; + int ret = 0; + + ENTER; + data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0); + if (data) { + ret = data[0]; + XFree (data); + } + RET(ret); +} + +static void +calculate_width(int scrw, int wtype, int allign, int margin, + int *panw, int *x) +{ + ENTER; + DBG("scrw=%d\n", scrw); + DBG("IN panw=%d\n", *panw); + //scrw -= 2; + if (wtype == WIDTH_PERCENT) { + /* sanity check */ + if (*panw > 100) + *panw = 100; + else if (*panw < 0) + *panw = 1; + *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0; + } + if (allign != ALLIGN_CENTER) { + if (margin > scrw) { + ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n", + margin, scrw); + margin = 0; + } + if (wtype == WIDTH_PERCENT) + //*panw = MAX(scrw - margin, *panw); + ; + else + *panw = MIN(scrw - margin, *panw); + } + DBG("OUT panw=%d\n", *panw); + if (allign == ALLIGN_LEFT) + *x += margin; + else if (allign == ALLIGN_RIGHT) { + *x += scrw - *panw - margin; + if (*x < 0) + *x = 0; + } else if (allign == ALLIGN_CENTER) + *x += (scrw - *panw) / 2; + RET(); +} + + +void +calculate_position(panel *np, int distance) +{ + int sswidth, ssheight, minx, miny; + GdkScreen *screen; + GdkDisplay *display; + GdkRectangle *monitorGeometry; + + ENTER; + + display = gdk_display_get_default (); + screen = gdk_display_get_screen(display,0); + + monitorGeometry = (GdkRectangle*) malloc(sizeof(GdkRectangle)); + gdk_screen_get_monitor_geometry(screen,np->monitor,monitorGeometry); + + sswidth = monitorGeometry->width - 1; + ssheight = monitorGeometry->height; + + minx = monitorGeometry->x; + miny = monitorGeometry->y; + + + if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) { + np->aw = np->width; + np->ax = minx; + calculate_width(sswidth, np->widthtype, np->allign, np->margin, + &np->aw, &np->ax); + np->ah = np->height; + np->ah = MIN(PANEL_HEIGHT_MAX, np->ah); + np->ah = MAX(PANEL_HEIGHT_MIN, np->ah); + np->ay = miny + ((np->edge == EDGE_TOP) ? 0+distance : (ssheight - np->ah - distance)); + + } else { + np->ah = np->width; + np->ay = miny; + calculate_width(ssheight, np->widthtype, np->allign, np->margin, + &np->ah, &np->ay); + np->aw = np->height; + np->aw = MIN(PANEL_HEIGHT_MAX, np->aw); + np->aw = MAX(PANEL_HEIGHT_MIN, np->aw); + np->ax = minx + ((np->edge == EDGE_LEFT) ? 0+distance : (sswidth - np->aw - distance)); + } + DBG("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah); + RET(); +} + + + +gchar * +expand_tilda(gchar *file) +{ + ENTER; + RET((file[0] == '~') ? + g_strdup_printf("%s%s", getenv("HOME"), file+1) + : g_strdup(file)); + +} + + + + + +Window +Select_Window(Display *dpy) +{ + int status; + Cursor cursor; + XEvent event; + Window target_win = None, root = RootWindow(dpy,DefaultScreen(dpy)); + int buttons = 0; + + ENTER; + /* Make the target cursor */ + cursor = XCreateFontCursor(dpy, XC_crosshair); + + /* Grab the pointer using target cursor, letting it room all over */ + status = XGrabPointer(dpy, root, False, + ButtonPressMask|ButtonReleaseMask, GrabModeSync, + GrabModeAsync, root, cursor, CurrentTime); + if (status != GrabSuccess) { + ERR("Can't grab the mouse."); + RET(None); + } + /* Let the user select a window... */ + while ((target_win == None) || (buttons != 0)) { + /* allow one more event */ + XAllowEvents(dpy, SyncPointer, CurrentTime); + XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event); + switch (event.type) { + case ButtonPress: + if (target_win == None) { + target_win = event.xbutton.subwindow; /* window selected */ + DBG("target win = 0x%x\n", target_win); + if (target_win == None) target_win = root; + } + buttons++; + break; + case ButtonRelease: + if (buttons > 0) /* there may have been some down before we started */ + buttons--; + break; + } + } + + XUngrabPointer(dpy, CurrentTime); /* Done with pointer */ + RET(target_win); +} + + +/* + * SuxPanel version 0.1 + * Copyright (c) 2003 Leandro Pereira + * + * This program may be distributed under the terms of GNU General + * Public License version 2. You should have received a copy of the + * license with this program; if not, please consult http://www.fsf.org/. + * + * This program comes with no warranty. Use at your own risk. + * + */ + + +GtkWidget * +gtk_image_new_from_file_scaled(const gchar *file, gint width, + gint height) +{ + GtkWidget *img; + + ENTER; + if (g_file_test(file, G_FILE_TEST_EXISTS)) { + GError *err = NULL; + GdkPixbuf *pb; + + pb = gdk_pixbuf_new_from_file(file, &err); + if (err || !pb) { + g_error_free(err); + } else { + GdkPixbuf *pb_scaled; + + pb_scaled = gdk_pixbuf_scale_simple(pb, width, height, + GDK_INTERP_BILINEAR); + + img = gtk_image_new_from_pixbuf(pb_scaled); + + gdk_pixbuf_unref(pb); + gdk_pixbuf_unref(pb_scaled); + + RET(img); + } + } + + img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, + GTK_ICON_SIZE_BUTTON); + + RET(img); +} + + +void +get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name) +{ + GtkWidget *b; + //gint focus_width; + //gint focus_pad; + + ENTER; + b = gtk_button_new(); + if (parent) + gtk_container_add(parent, b); + gtk_widget_set_name(GTK_WIDGET(b), name); + GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT); + gtk_container_set_border_width (GTK_CONTAINER (b), 0); + + gtk_widget_show(b); + gtk_widget_size_request(b, req); + + gtk_widget_destroy(b); + RET(); +} + + + + diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..d8d2fc2 --- /dev/null +++ b/misc.h @@ -0,0 +1,55 @@ +#ifndef MISC_H +#define MISC_H + +#include +#include +#include +#include +#include + +#include "panel.h" + +enum { LINE_NONE, LINE_BLOCK_START, LINE_BLOCK_END, LINE_VAR }; + +typedef struct { + int num, len, type; + gchar str[256]; + gchar *t[3]; +} line; + + +typedef struct { + int num; + gchar *str; +} pair; + +extern pair allign_pair[]; +extern pair edge_pair[]; +extern pair width_pair[]; +extern pair height_pair[]; +extern pair bool_pair[]; +extern pair pos_pair[]; + +int str2num(pair *p, gchar *str, int defval); +gchar *num2str(pair *p, int num, gchar *defval); +int get_line(FILE *fp, line *s); +int get_line_as_is(FILE *fp, line *s); + +void Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4); +void *get_xaproperty (Window win, Atom prop, Atom type, int *nitems); +char *get_textproperty(Window win, Atom prop); +void resolve_atoms(); +Window Select_Window(Display *dpy); +int get_net_number_of_desktops(); +int get_net_current_desktop (); +int get_net_wm_desktop(Window win); +int get_wm_state (Window win); +void get_net_wm_state(Window win, net_wm_state *nws); +void get_net_wm_window_type(Window win, net_wm_window_type *nwwt); + +void calculate_position(panel *np, int distance); +gchar *expand_tilda(gchar *file); +GtkWidget *gtk_image_new_from_file_scaled(const gchar *file, gint width, gint height); +void get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name); + +#endif diff --git a/panel.c b/panel.c new file mode 100644 index 0000000..f267f13 --- /dev/null +++ b/panel.c @@ -0,0 +1,875 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "panel.h" +#include "misc.h" +#include "bg.h" + +/* do not change this line - Makefile's 'tar' target depends on it */ +#define VERSION "1.0" + +static gchar *cfgfile = NULL; +static gchar version[] = VERSION; +static gchar *cprofile = "default"; +int distance=0; +int expand=1 , padding=0; + + +//#define DEBUG +#include "dbg.h" + + +static panel *p; +static gchar *transparent_rc = "style 'transparent-style'\n" +"{\n" +"bg_pixmap[NORMAL] = \"\"\n" +"bg_pixmap[INSENSITIVE] = \"\"\n" +"bg_pixmap[PRELIGHT] = \"\"\n" +"bg_pixmap[SELECTED] = \"\"\n" +"bg_pixmap[ACTIVE] = \"\"\n" +"}\n" +"class \"GtkEventBox\" style \"transparent-style\"\n" +"class \"GtkSocket\" style \"transparent-style\"\n" +"class \"GtkBar\" style \"transparent-style\"\n" +"class \"GtkBox\" style \"transparent-style\"\n" +"\n"; + + +/* +"class \"GtkBox\" style \"transparent-style\"\n" +"class \"GtkContainer\" style \"transparent-style\"\n" +"class \"GtkBin\" style \"transparent-style\"\n" +"class \"GtkSeparator\" style \"transparent-style\"\n" +*/ + +static void set_bg(GtkWidget *widget, panel *p); + +/**************************************************** + * panel's handlers for WM events * + ****************************************************/ +/* +static void +panel_del_wm_strut(panel *p) +{ + XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT); + XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL); +} +*/ + +static void +panel_set_wm_strut(panel *p) +{ + unsigned long data[12] = { 0 }; + int i = 4; + + ENTER; + if (!GTK_WIDGET_MAPPED (p->topgwin)) + return; + switch (p->edge) { + case EDGE_LEFT: + i = 0; + data[i] = p->aw; + data[4 + i*2] = p->ay; + data[5 + i*2] = p->ay + p->ah; + break; + case EDGE_RIGHT: + i = 1; + data[i] = p->aw; + data[4 + i*2] = p->ay; + data[5 + i*2] = p->ay + p->ah; + break; + case EDGE_TOP: + i = 2; + data[i] = p->ah; + data[4 + i*2] = p->ax; + data[5 + i*2] = p->ax + p->aw; + break; + case EDGE_BOTTOM: + i = 3; + data[i] = p->ah; + data[4 + i*2] = p->ax; + data[5 + i*2] = p->ax + p->aw; + break; + default: + ERR("wrong edge %d. strut won't be set\n", p->edge); + RET(); + } + DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]); + + /* if wm supports STRUT_PARTIAL it will ignore STRUT */ + XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL, + XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12); + /* old spec, for wms that do not support STRUT_PARTIAL */ + XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT, + XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4); + + RET(); +} + +static void +print_wmdata(panel *p) +{ + int i; + + ENTER; + DBG("desktop %d/%d\n", p->curdesk, p->desknum); + DBG("workarea\n"); + for (i = 0; i < p->wa_len/4; i++) + DBG("(%d, %d) x (%d, %d)\n", + p->workarea[4*i + 0], + p->workarea[4*i + 1], + p->workarea[4*i + 2], + p->workarea[4*i + 3]); + RET(); +} + + +static GdkFilterReturn +panel_wm_events(GdkXEvent *xevent, GdkEvent *event, panel *p) +{ + Atom at; + Window win; + XEvent *ev = (XEvent *) xevent; + + ENTER; + DBG("win = 0x%x\n", ev->xproperty.window); + if (ev->type != PropertyNotify ) + RET(GDK_FILTER_CONTINUE); + + at = ev->xproperty.atom; + win = ev->xproperty.window; + if (win == GDK_ROOT_WINDOW()) { + if (at == a_NET_CLIENT_LIST) { + DBG("A_NET_CLIENT_LIST\n"); + } else if (at == a_NET_CURRENT_DESKTOP) { + p->curdesk = get_net_current_desktop(); + DBG("A_NET_CURRENT_DESKTOP\n"); + } else if (at == a_NET_NUMBER_OF_DESKTOPS) { + p->desknum = get_net_number_of_desktops(); + DBG("A_NET_NUMBER_OF_DESKTOPS\n"); + } else if (at == a_NET_ACTIVE_WINDOW) { + DBG("A_NET_ACTIVE_WINDOW\n"); + } else if (at == a_XROOTPMAP_ID) { + bg_rootbg_changed(); + set_bg(p->topgwin, p); + gtk_widget_queue_draw(p->topgwin); + DBG("a_XROOTPMAP_ID\n"); + } else if (at == a_NET_WORKAREA) { + DBG("A_NET_WORKAREA\n"); + p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len); + print_wmdata(p); + } + } + RET(GDK_FILTER_CONTINUE); +} + +/**************************************************** + * panel's handlers for GTK events * + ****************************************************/ + + +static gint +panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data) +{ + ENTER; + RET(FALSE); +} + +static gint +panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data) +{ + //panel *p = (panel *) data; + + ENTER; + //if (!p->self_destroy) + gtk_main_quit(); + RET(FALSE); +} + + + +static gint +panel_size_req(GtkWidget *widget, GtkRequisition *req, panel *p) +{ + ENTER; + DBG("IN req=(%d, %d)\n", req->width, req->height); + if (p->widthtype == WIDTH_REQUEST) + p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height; + if (p->heighttype == HEIGHT_REQUEST) + p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width; + calculate_position(p, distance); + req->width = p->aw; + req->height = p->ah; + DBG("OUT req=(%d, %d)\n", req->width, req->height); + RET( TRUE ); +} + +static gint +panel_size_alloc(GtkWidget *widget, GtkAllocation *a, panel *p) +{ + ENTER; + DBG("new alloc: size (%d, %d). pos (%d, %d)\n", a->width, a->height, a->x, a->y); + DBG("old alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay); + if (p->widthtype == WIDTH_REQUEST) + p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height; + if (p->heighttype == HEIGHT_REQUEST) + p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width; + calculate_position(p, distance); + DBG("pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay); + if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) { + DBG("actual coords eq to preffered. just returning\n"); + RET(TRUE); + } + + /* + if (p->setstrut) { + panel_del_wm_strut(p); + gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay); + panel_set_wm_strut(p); + } else { + gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay); + } + */ + gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay); + if (p->setstrut) + panel_set_wm_strut(p); + //gdk_window_clear(p->topgwin->window); + RET(TRUE); +} + + + + +/**************************************************** + * panel creation * + ****************************************************/ +static void +make_round_corners(panel *p) +{ + GtkWidget *b1, *b2, *img; + GtkWidget *(*box_new) (gboolean, gint); + void (*box_pack)(GtkBox *, GtkWidget *, gboolean, gboolean, guint); + gchar *s1, *s2; +#define IMGPREFIX PREFIX "/share/trayer/images/" + + ENTER; + if (p->edge == EDGE_TOP) { + s1 = IMGPREFIX "top-left.xpm"; + s2 = IMGPREFIX "top-right.xpm"; + } else if (p->edge == EDGE_BOTTOM) { + s1 = IMGPREFIX "bottom-left.xpm"; + s2 = IMGPREFIX "bottom-right.xpm"; + } else if (p->edge == EDGE_LEFT) { + s1 = IMGPREFIX "top-left.xpm"; + s2 = IMGPREFIX "bottom-left.xpm"; + } else if (p->edge == EDGE_RIGHT) { + s1 = IMGPREFIX "top-right.xpm"; + s2 = IMGPREFIX "bottom-right.xpm"; + } else + RET(); + + box_new = (p->orientation == ORIENT_HORIZ) ? gtk_vbox_new : gtk_hbox_new; + b1 = box_new(0, FALSE); + gtk_widget_show(b1); + b2 = box_new(0, FALSE); + gtk_widget_show(b2); + + box_pack = (p->edge == EDGE_TOP || p->edge == EDGE_LEFT) ? + gtk_box_pack_start : gtk_box_pack_end; + + img = gtk_image_new_from_file(s1); + gtk_widget_show(img); + box_pack(GTK_BOX(b1), img, FALSE, FALSE, 0); + img = gtk_image_new_from_file(s2); + gtk_widget_show(img); + box_pack(GTK_BOX(b2), img, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(p->lbox), b1, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(p->lbox), b2, FALSE, FALSE, 0); + RET(); +} + +static void +set_bg(GtkWidget *widget, panel *p) +{ + ENTER; + /* + if (p->xtopbg != None) + XFreePixmap(GDK_DISPLAY(), p->xtopbg); + p->xtopbg = bg_new_for_win(p->topxwin); + if (p->gtopbg) + g_object_unref(G_OBJECT(p->gtopbg)); + p->gtopbg = gdk_xid_table_lookup (p->xtopbg); + if (p->gtopbg) + g_object_ref (G_OBJECT (p->gtopbg)); + else + p->gtopbg = gdk_pixmap_foreign_new (p->xtopbg); + */ + if (p->gtopbg) + g_object_unref(p->gtopbg); + p->gtopbg = bg_new_for_win(p->topxwin); + + + modify_drawable(p->gtopbg, p->topgwin->style->black_gc, p->tintcolor, p->alpha); + + gdk_window_set_back_pixmap(p->topgwin->window, p->gtopbg, FALSE); + gdk_window_clear(p->topgwin->window); + gtk_widget_queue_draw_area (p->topgwin, 0, 0, 2000, 2000); + RET(); +} + +static void +panel_style_set(GtkWidget *widget, GtkStyle *s, panel *p) +{ + ENTER; + + gtk_rc_parse_string(transparent_rc); + if (GTK_WIDGET_REALIZED(widget)) + set_bg(widget, p); + RET(); +} + +static gboolean +panel_configure_event(GtkWidget *widget, GdkEventConfigure *event, panel *p) +{ + static gint x = 0, y = 0, width = 0, height = 0; + + ENTER; + if (x == event->x && y == event->y + && width == event->width && height == event->height) + RET(FALSE); + x = event->x; + y = event->y; + width = event->width; + height = event->height; + set_bg(widget, p); + RET(FALSE); +} + +void +panel_start_gui(panel *p) +{ + Atom state[3]; + XWMHints wmhints; + unsigned int val; + + + ENTER; + //gtk_rc_parse_string(transparent_rc); + p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + // TOOD set color + //gtk_widget_modify_bg(p->topgwin,GTK_STATE_NORMAL,color) + gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0); + gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE); + gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "trayer"); + gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel"); + g_signal_connect(G_OBJECT(p->topgwin), "delete-event", + G_CALLBACK(panel_delete_event), p); + g_signal_connect(G_OBJECT(p->topgwin), "destroy-event", + G_CALLBACK(panel_destroy_event), p); + g_signal_connect (G_OBJECT (p->topgwin), "size-request", + (GCallback) panel_size_req, p); + g_signal_connect (G_OBJECT (p->topgwin), "size-allocate", + (GCallback) panel_size_alloc, p); + + if (p->transparent) { + g_signal_connect (G_OBJECT (p->topgwin), "configure-event", + (GCallback) panel_configure_event, p); + + g_signal_connect (G_OBJECT (p->topgwin), "style-set", + (GCallback) panel_style_set, p); + } + gtk_widget_realize(p->topgwin); + gdk_window_set_decorations(p->topgwin->window, 0); + gtk_widget_set_app_paintable(p->topgwin, TRUE); + + p->lbox = p->my_box_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(p->lbox), 0); + gtk_container_add(GTK_CONTAINER(p->topgwin), p->lbox); + gtk_widget_show(p->lbox); + if (p->round_corners) + make_round_corners(p); + + p->box = p->my_box_new(FALSE, 1); + gtk_container_set_border_width(GTK_CONTAINER(p->box), 1); + gtk_box_pack_start(GTK_BOX(p->lbox), p->box, TRUE, TRUE, 0); + gtk_widget_show(p->box); + + p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window); + + bg_init(GDK_DISPLAY()); + + /* make our window unfocusable */ + wmhints.flags = InputHint; + wmhints.input = 0; + XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints); + if (p->setdocktype) { + state[0] = a_NET_WM_WINDOW_TYPE_DOCK; + XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE, XA_ATOM, + 32, PropModeReplace, (unsigned char *) state, 1); + } + + + +#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */ + val = WIN_HINTS_SKIP_FOCUS; + XChangeProperty(GDK_DISPLAY(), p->topxwin, + XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) &val, 1); + + Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0); + + /************************/ + /* Window Mapping Point */ + gtk_widget_show_all(p->topgwin); + Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0); + + state[0] = a_NET_WM_STATE_SKIP_PAGER; + state[1] = a_NET_WM_STATE_SKIP_TASKBAR; + state[2] = a_NET_WM_STATE_STICKY; + XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM, + 32, PropModeReplace, (unsigned char *) state, 3); + + + + XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), PropertyChangeMask); + /* + XSelectInput (GDK_DISPLAY(), topxwin, PropertyChangeMask | FocusChangeMask | + StructureNotifyMask); + */ + gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_wm_events, p); + + calculate_position(p, distance); + gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah); + if (p->setstrut) + panel_set_wm_strut(p); + + + RET(); +} + +static int +panel_parse_global(panel *p) +{ + ENTER; + p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM) + ? ORIENT_HORIZ : ORIENT_VERT; + if (p->orientation == ORIENT_HORIZ) { + p->my_box_new = gtk_hbox_new; + p->my_separator_new = gtk_vseparator_new; + } else { + p->my_box_new = gtk_vbox_new; + p->my_separator_new = gtk_hseparator_new; + } + if (p->width < 0) + p->width = 100; + if (p->widthtype == WIDTH_PERCENT && p->width > 100) + p->width = 100; + p->heighttype = HEIGHT_PIXEL; + if (p->heighttype == HEIGHT_PIXEL) { + if (p->height < PANEL_HEIGHT_MIN) + p->height = PANEL_HEIGHT_MIN; + else if (p->height > PANEL_HEIGHT_MAX) + p->height = PANEL_HEIGHT_MAX; + } + p->curdesk = get_net_current_desktop(); + p->desknum = get_net_number_of_desktops(); + p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len); + print_wmdata(p); + panel_start_gui(p); + RET(1); +} + +static int +panel_parse_plugin(panel *p) +{ + plugin *plug = NULL; + FILE *tmpfp; + + ENTER; + if (!(tmpfp = tmpfile())) { + ERR( "can't open temporary file with tmpfile()\n"); + RET(0); + } + + if (!(plug = plugin_load("tray"))) { + ERR( "trayer: can't load systray\n" ); + goto error; + } + plug->panel = p; + plug->fp = tmpfp; + plug->expand = expand; + plug->padding = padding; + fprintf(tmpfp, "}\n"); + fseek(tmpfp, 0, SEEK_SET); + if (!plugin_start(plug)) { + ERR( "trayer: can't start systray\n" ); + goto error; + } + DBG("systray\n"); + p->plugins = g_list_append(p->plugins, plug); + RET(1); + + error: + fclose(tmpfp); + if (plug) + plugin_put(plug); + RET(0); + +} + + +int +panel_start(panel *p) +{ + line s; + + /* parse global section */ + ENTER; + s.len = 256; + + if (!panel_parse_global(p)) + RET(0); + + if (!panel_parse_plugin(p)) + RET(0); + + gtk_widget_show_all(p->topgwin); + print_wmdata(p); + RET(1); +} + +static void +delete_plugin(gpointer data, gpointer udata) +{ + ENTER; + plugin_stop((plugin *)data); + plugin_put((plugin *)data); + RET(); + +} + +void panel_stop(panel *p) +{ + ENTER; + + g_list_foreach(p->plugins, delete_plugin, NULL); + g_list_free(p->plugins); + p->plugins = NULL; + XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask); + gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_wm_events, p); + gtk_widget_destroy(p->topgwin); + g_free(p->workarea); + RET(); +} + + +void +usage() +{ + ENTER; + printf("trayer %s - lightweight GTK2+ systray for UNIX desktops\n", version); + printf("Command line options:\n"); + printf(" -h -- print this help and exit:\n"); + printf(" -v -- print version and exit:\n"); + printf(" --edge \n"); + printf(" --align \n"); + printf(" --margin \n"); + printf(" --widthtype \n"); + printf(" --width \n"); + printf(" --heighttype \n"); + printf(" --height \n"); + printf(" --SetDockType \n"); + printf(" --SetPartialStrut \n"); + printf(" --transparent \n"); + printf(" --alpha \n"); + printf(" --tint \n"); + printf(" --distance \n"); + printf(" --expand \n"); + printf(" --padding \n"); + printf(" --monitor \n"); + /*printf(" -p -- use named profile. File ~/.trayer/ must exist\n"); + printf("\nVisit http://fbpanel.sourceforge.net/ for detailed documentation,\n"); + printf("sample profiles and other stuff.\n\n");*/ +} + +FILE * +open_profile(gchar *profile) +{ + gchar *fname; + FILE *fp; + + ENTER; + fname = g_strdup_printf("%s/.trayer/%s", getenv("HOME"), profile); + if ((fp = fopen(fname, "r"))) { + cfgfile = fname; + ERR("Using %s\n", fname); + RET(fp); + } + ERR("Can't load %s\n", fname); + g_free(fname); + + /* check private configuration directory */ + fname = g_strdup_printf("%s/share/trayer/%s", PREFIX, profile); + if ((fp = fopen(fname, "r"))) { + cfgfile = fname; + ERR("Using %s\n", fname); + RET(fp); + } + ERR("Can't load %s\n", fname); + g_free(fname); + ERR("Can't open '%s' profile\n", profile); + RET(NULL); +} + +void +handle_error(Display * d, XErrorEvent * ev) +{ + char buf[256]; + + ENTER; + XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256); + ERR( "trayer : X error: %s\n", buf); + RET(); +} + +/* +static void +sig_usr(int signum) +{ + if (signum != SIGUSR1) + return; + gtk_main_quit(); +} +*/ + +int +main(int argc, char *argv[], char *env[]) +{ + int i; + + ENTER; + setlocale(LC_CTYPE, ""); + gtk_set_locale(); + gtk_init(&argc, &argv); + XSetLocaleModifiers(""); + XSetErrorHandler((XErrorHandler) handle_error); + resolve_atoms(); + + p = g_new0(panel, 1); + memset(p, 0, sizeof(panel)); + p->allign = ALLIGN_CENTER; + p->edge = EDGE_BOTTOM; + p->widthtype = WIDTH_PERCENT; + p->width = 100; + p->heighttype = HEIGHT_PIXEL; + p->height = PANEL_HEIGHT_DEFAULT; + p->setdocktype = 1; + p->setstrut = 0; + p->round_corners = 0; + p->transparent = 0; + p->alpha = 127; + p->tintcolor = 0xFFFFFFFF; + p->xtopbg = None; + p->monitor = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { + usage(); + exit(0); + } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) { + printf("trayer %s\n", version); + exit(0); +/* + } else if (!strcmp(argv[i], "--verbose")) { + verbose = 1; + } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) { + i++; + if (i == argc) { + ERR( "trayer: missing profile name\n"); + usage(); + exit(1); + } else { + cprofile = g_strdup(argv[i]); + } +*/ + } else if (!strcmp(argv[i], "--edge")) { + i++; + if (i == argc) { + ERR( "trayer: missing edge parameter value\n"); + usage(); + exit(1); + } else { + p->edge = str2num(edge_pair, argv[i], EDGE_NONE); + } + } else if (!strcmp(argv[i], "--align")) { + i++; + if (i == argc) { + ERR( "trayer: missing align parameter value\n"); + usage(); + exit(1); + } else { + p->allign = str2num(allign_pair, argv[i], ALLIGN_NONE); + } + } else if (!strcmp(argv[i], "--margin")) { + i++; + if (i == argc) { + ERR( "trayer: missing margin parameter value\n"); + usage(); + exit(1); + } else { + p->margin = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--widthtype")) { + i++; + if (i == argc) { + ERR( "trayer: missing widthtype parameter value\n"); + usage(); + exit(1); + } else { + p->widthtype = str2num(width_pair, argv[i], WIDTH_NONE); + } + } else if (!strcmp(argv[i], "--width")) { + i++; + if (i == argc) { + ERR( "trayer: missing width parameter value\n"); + usage(); + exit(1); + } else { + p->width = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--heighttype")) { + i++; + if (i == argc) { + ERR( "trayer: missing heighttype parameter value\n"); + usage(); + exit(1); + } else { + p->heighttype = str2num(height_pair, argv[i], HEIGHT_NONE); + } + } else if (!strcmp(argv[i], "--height")) { + i++; + if (i == argc) { + ERR( "trayer: missing height parameter value\n"); + usage(); + exit(1); + } else { + p->height = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--SetDockType")) { + i++; + if (i == argc) { + ERR( "trayer: missing SetDockType parameter value\n"); + usage(); + exit(1); + } else { + p->setdocktype = str2num(bool_pair, argv[i], 0); + } + } else if (!strcmp(argv[i], "--SetPartialStrut")) { + i++; + if (i == argc) { + ERR( "trayer: missing SetPartialStrut parameter value\n"); + usage(); + exit(1); + } else { + p->setstrut = str2num(bool_pair, argv[i], 0); + } + } else if (!strcmp(argv[i], "--RoundCorners")) { + i++; + if (i == argc) { + ERR( "trayer: missing RoundCorners parameter value\n"); + usage(); + exit(1); + } else { + p->round_corners = str2num(bool_pair, argv[i], 0); + } + } else if (!strcmp(argv[i], "--transparent")) { + i++; + if (i == argc) { + ERR( "trayer: missing transparent parameter value\n"); + usage(); + exit(1); + } else { + p->transparent = str2num(bool_pair, argv[i], 1); + } + } else if (!strcmp(argv[i], "--alpha")) { + i++; + if (i == argc) { + ERR( "trayer: missing alpha parameter value\n"); + usage(); + exit(1); + } else { + p->alpha = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--tint")) { + i++; + if (i == argc) { + ERR( "trayer: missing tint parameter value\n"); + usage(); + exit(1); + } else { + p->tintcolor = strtoul(argv[i], NULL, 0); + } + } else if (!strcmp(argv[i], "--distance")) { + i++; + if (i == argc) { + ERR( "trayer: missing distance parameter value\n"); + usage(); + exit(1); + } else { + distance = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--expand")) { + i++; + if (i == argc) { + ERR( "trayer: missing expand parameter value\n"); + usage(); + exit(1); + } else { + expand = str2num(bool_pair, argv[i], 1); + } + } else if (!strcmp(argv[i], "--padding")) { + i++; + if (i == argc) { + ERR( "trayer: missing padding parameter value\n"); + usage(); + exit(1); + } else { + padding = atoi(argv[i]); + } + } else if (!strcmp(argv[i], "--monitor")) { + i++; + if (i == argc) { + ERR( "trayer: missing monitor parameter value\n"); + usage(); + exit(1); + } else { + p->monitor = atoi(argv[i]); + } + } else { + printf("trayer: unknown option - %s\n", argv[i]); + usage(); + exit(1); + } + } + g_return_val_if_fail (p != NULL, 1); + if (!panel_start(p)) { + ERR( "trayer: can't start panel\n"); + exit(1); + } + gtk_main(); + panel_stop(p); + g_free(p); + + exit(0); +} + diff --git a/panel.h b/panel.h new file mode 100644 index 0000000..af08aea --- /dev/null +++ b/panel.h @@ -0,0 +1,128 @@ +#ifndef PANEL_H +#define PANEL_H + + +#include +#include +#include + +#include "config.h" + +enum { ALLIGN_NONE, ALLIGN_LEFT, ALLIGN_RIGHT, ALLIGN_CENTER }; +enum { EDGE_NONE, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_BOTTOM }; +enum { WIDTH_NONE, WIDTH_REQUEST, WIDTH_PIXEL, WIDTH_PERCENT }; +enum { HEIGHT_NONE, HEIGHT_PIXEL, HEIGHT_REQUEST }; +enum { ORIENT_NONE, ORIENT_VERT, ORIENT_HORIZ }; +enum { POS_NONE, POS_START, POS_END }; + +#define PANEL_HEIGHT_DEFAULT 26 +#define PANEL_HEIGHT_MAX 200 +#define PANEL_HEIGHT_MIN 16 + + +typedef struct { + + GtkWidget *topgwin; /* main panel window */ + Window topxwin; /* and it X window */ + GtkWidget *lbox; /* primary layout box */ + GtkWidget *box; /* box that contains all plugins */ + GtkRequisition requisition; + GtkWidget *(*my_box_new) (gboolean, gint); + GtkWidget *(*my_separator_new) (); + Pixmap xtopbg; + GdkPixmap *gtopbg; + int alpha; + guint32 tintcolor; + + int ax, ay, aw, ah; /* actual location and size of a panel */ + int allign, edge, margin; + int orientation; + int widthtype, width; + int heighttype, height; + + int self_destroy : 1; + int setdocktype : 1; + int setstrut : 1; + int round_corners : 1; + int transparent : 1; + int monitor; + + int desknum; + int curdesk; + unsigned int *workarea; + int wa_len; + + int plug_num; + GList *plugins; + +} panel; + + +typedef struct { + unsigned int modal : 1; + unsigned int sticky : 1; + unsigned int maximized_vert : 1; + unsigned int maximized_horz : 1; + unsigned int shaded : 1; + unsigned int skip_taskbar : 1; + unsigned int skip_pager : 1; + unsigned int hidden : 1; + unsigned int fullscreen : 1; + unsigned int above : 1; + unsigned int below : 1; +} net_wm_state; + +typedef struct { + unsigned int desktop : 1; + unsigned int dock : 1; + unsigned int toolbar : 1; + unsigned int menu : 1; + unsigned int utility : 1; + unsigned int splash : 1; + unsigned int dialog : 1; + unsigned int normal : 1; +} net_wm_window_type; + + +extern Atom a_UTF8_STRING; +extern Atom a_XROOTPMAP_ID; + +extern Atom a_WM_STATE; +extern Atom a_WM_CLASS; + +extern Atom a_NET_WORKAREA; +extern Atom a_NET_CLIENT_LIST; +extern Atom a_NET_CLIENT_LIST_STACKING; +extern Atom a_NET_NUMBER_OF_DESKTOPS; +extern Atom a_NET_CURRENT_DESKTOP; +extern Atom a_NET_DESKTOP_NAMES; +extern Atom a_NET_ACTIVE_WINDOW; +extern Atom a_NET_WM_STATE; +extern Atom a_NET_WM_STATE_SKIP_TASKBAR; +extern Atom a_NET_WM_STATE_SKIP_PAGER; +extern Atom a_NET_WM_STATE_STICKY; +extern Atom a_NET_WM_STATE_HIDDEN; +extern Atom a_NET_WM_STATE_SHADED; + +#define a_NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define a_NET_WM_STATE_ADD 1 /* add/set property */ +#define a_NET_WM_STATE_TOGGLE 2 /* toggle property */ + +extern Atom a_NET_WM_WINDOW_TYPE; +extern Atom a_NET_WM_WINDOW_TYPE_DESKTOP; +extern Atom a_NET_WM_WINDOW_TYPE_DOCK; +extern Atom a_NET_WM_WINDOW_TYPE_TOOLBAR; +extern Atom a_NET_WM_WINDOW_TYPE_MENU; +extern Atom a_NET_WM_WINDOW_TYPE_UTILITY; +extern Atom a_NET_WM_WINDOW_TYPE_SPLASH; +extern Atom a_NET_WM_WINDOW_TYPE_DIALOG; +extern Atom a_NET_WM_WINDOW_TYPE_NORMAL; + +extern Atom a_NET_WM_DESKTOP; +extern Atom a_NET_WM_NAME; +extern Atom a_NET_WM_STRUT; +extern Atom a_NET_WM_STRUT_PARTIAL; + +extern Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR; + +#endif diff --git a/plugin.c b/plugin.c new file mode 100644 index 0000000..7fd5398 --- /dev/null +++ b/plugin.c @@ -0,0 +1,195 @@ + +#include "plugin.h" + +#include +#include +#include +#include + + +//#define DEBUG +#include "dbg.h" +#include "misc.h" + +static GList *pcl = NULL; + + +/* counter for static (built-in) plugins must be greater then zero + * so fbpanel will not try to unload them */ +#define STATIC_PLUGIN_CLASS(spc) \ +do { \ +extern plugin_class spc;\ +pcl = g_list_append(pcl, &spc);\ +spc.count++;\ +spc.dynamic = 0;\ +} while (0) + + +static void +init_plugin_class_list() +{ + ENTER; + +#ifdef STATIC_TRAY + STATIC_PLUGIN_CLASS(tray_plugin_class); +#endif + + RET(); +} + + + + + +plugin * +plugin_load(char *type) +{ + GList *tmp; + plugin_class *pc = NULL; + plugin *plug = NULL; + static GString *str = NULL; + + ENTER; + if (!pcl) + init_plugin_class_list(); + + for (tmp = pcl; tmp; tmp = g_list_next(tmp)) { + pc = (plugin_class *) tmp->data; + if (!g_ascii_strcasecmp(type, pc->type)) { + break; + } + } + if (!tmp && g_module_supported()) { + GModule *m; + + if (!str) + str = g_string_sized_new(PATH_MAX); + g_string_printf(str, "%s/share/trayer/plugins/%s.so", PREFIX, type); + m = g_module_open(str->str, G_MODULE_BIND_LOCAL | G_MODULE_BIND_LAZY); + if (!m) + RET(NULL); + + g_string_printf(str, "%s_plugin_class", type); + if (!g_module_symbol(m, str->str, (gpointer *)&pc)) + RET(NULL); + pc->gmodule = m; + pc->dynamic = 1; + } + + /* nothing was found */ + if (!pc) + RET(NULL); + + plug = g_new0(plugin, 1); + g_return_val_if_fail (plug != NULL, NULL); + plug->class = pc; + pc->count++; + RET(plug); +} + + +void plugin_put(plugin *this) +{ + ENTER; + if (this->class->dynamic) + g_module_close(this->class->gmodule); + //if (this->class->count == 0) + //ERR( "unloading plugin %s\n", this->class->type); + g_free(this); + RET(); +} + + +#if 0 +static void +plugin_style_set(GtkWidget *widget, GtkStyle *s, plugin *p) +{ + ENTER; + /* + if (p->double_buffered) + gtk_widget_set_double_buffered (widget, FALSE); + if (GTK_WIDGET_REALIZED(widget)) { + gdk_window_clear(widget->window); + + if (p->double_buffered) + gtk_widget_set_double_buffered (widget, TRUE); + gtk_widget_queue_draw(widget); + */ + RET(); + +} + +static gint +plugin_size_alloc(GtkWidget *widget, GtkAllocation *a, plugin *p) +{ + static GtkAllocation a2; + + ENTER; + if (memcmp(&a2, a, sizeof(GtkAllocation))) { + GdkRectangle rect; + + gdk_window_clear(widget->window); + + rect.x = rect.y = 0; + rect.width = widget->allocation.width; + rect.height = widget->allocation.height; + gdk_window_invalidate_rect(widget->window, &rect, TRUE); + gtk_style_apply_default_background (widget->style, widget->window, TRUE, widget->state, + &rect, 0, 0, rect.width, rect.height); + a2 = *a; + DBG("size changed for %s\n", p->class->type); + + /* + gtk_widget_set_double_buffered (widget, FALSE); + gdk_window_clear(widget->window); + gtk_widget_queue_draw(widget); + //gtk_widget_set_double_buffered (widget, TRUE); + p->double_buffered = 0; + */ + } + RET(TRUE); +} + +#endif + + +int +plugin_start(plugin *this) +{ + //panel *p = this->panel; + ENTER; + this->pwid = gtk_event_box_new(); + if (this->panel->transparent) { + /* + g_signal_connect (G_OBJECT (this->pwid), "style-set", + (GCallback) plugin_style_set, this); + g_signal_connect (G_OBJECT (this->pwid), "size-allocate", + (GCallback) plugin_size_alloc, this); + this->double_buffered = 0; + //gtk_widget_set_double_buffered (this->pwid, FALSE); + */ + } + gtk_box_pack_start(GTK_BOX(this->panel->box), this->pwid, this->expand, TRUE, + this->padding); + + if (!this->class->constructor(this)) { + gtk_widget_destroy(this->pwid); + RET(0); + } + gtk_widget_show(this->pwid); + + //g_timeout_add(3000, (GSourceFunc) clear, (gpointer)this->pwid); + //gdk_window_clear(this->pwid->window); + RET(1); +} + + +void plugin_stop(plugin *this) +{ + ENTER; + this->class->destructor(this); + this->panel->plug_num--; + gtk_widget_destroy(this->pwid); + RET(); +} + diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..2c7bdb3 --- /dev/null +++ b/plugin.h @@ -0,0 +1,62 @@ + +#ifndef PLUGIN_H +#define PLUGIN_H +#include + + +#include +#include +#include +#include "panel.h" + +struct _plugin *stam; + +typedef struct { + /* common */ + char *fname; + int count; + int dynamic; + GModule *gmodule; + + /* these fields are pointers to the data within loaded dll */ + char *type; + char *name; + char *version; + char *description; + + int (*constructor)(struct _plugin *this); + void (*destructor)(struct _plugin *this); + +} plugin_class; + +typedef struct _plugin{ + plugin_class *class; + panel *panel; + FILE *fp; + GtkWidget *pwid; + int expand; + int padding; + gpointer priv; + int double_buffered; +} plugin; + +/* if plugin is external it will load its dll */ +plugin * plugin_load(char *type); +void plugin_put(plugin *this); +int plugin_start(plugin *this); +void plugin_stop(plugin *this); + + +#define STATIC_SEPARATOR +#define STATIC_IMAGE +#define STATIC_LAUNCHBAR +#define STATIC_DCLOCK +#define STATIC_WINCMD +#define STATIC_TEST +#define STATIC_TASKBAR +#define STATIC_PAGER +#define STATIC_TRAY +#define STATIC_MENU +//#define STATIC_HANDLE + +#endif diff --git a/systray/Makefile b/systray/Makefile new file mode 100644 index 0000000..270c6ef --- /dev/null +++ b/systray/Makefile @@ -0,0 +1,23 @@ +# Part 0 +# load common stuff +TOPDIR = .. +include $(TOPDIR)/Makefile.common +#$(warning INCS=$(INCS)) + +INCS += -I../ +SRC := egg-marshal.c eggtraymanager.c fixedtip.c main.c +OBJ := $(SRC:%.c=%.o) + +TARGET = systray.o + +all:$(TARGET) +$(TARGET): $(OBJ) + $(LD) -r $(OBJ) -o $@ + +%.o: %.c + $(CC) $(CFLAGS) $(INCS) -c $< + + +clean: + $(RM) $(OBJ) $(TARGET) *~ + diff --git a/systray/egg-marshal.c b/systray/egg-marshal.c new file mode 100644 index 0000000..2fb5bb4 --- /dev/null +++ b/systray/egg-marshal.c @@ -0,0 +1,2 @@ +#include "eggmarshalers.h" +#include "eggmarshalers.c" diff --git a/systray/eggmarshalers.c b/systray/eggmarshalers.c new file mode 100644 index 0000000..a9b03e9 --- /dev/null +++ b/systray/eggmarshalers.c @@ -0,0 +1,164 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_int +#define g_marshal_value_peek_flags(v) (v)->data[0].v_uint +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:OBJECT,OBJECT (eggmarshalers.list:1) */ +void +_egg_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + +/* VOID:OBJECT,STRING,LONG,LONG (eggmarshalers.list:2) */ +void +_egg_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + glong arg_3, + glong arg_4, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + g_marshal_value_peek_long (param_values + 3), + g_marshal_value_peek_long (param_values + 4), + data2); +} + +/* VOID:OBJECT,LONG (eggmarshalers.list:3) */ +void +_egg_marshal_VOID__OBJECT_LONG (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_LONG) (gpointer data1, + gpointer arg_1, + glong arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_LONG callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_LONG) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_long (param_values + 2), + data2); +} + diff --git a/systray/eggmarshalers.h b/systray/eggmarshalers.h new file mode 100644 index 0000000..4ac58bf --- /dev/null +++ b/systray/eggmarshalers.h @@ -0,0 +1,36 @@ + +#ifndef ___egg_marshal_MARSHAL_H__ +#define ___egg_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:OBJECT,OBJECT (eggmarshalers.list:1) */ +extern void _egg_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT,STRING,LONG,LONG (eggmarshalers.list:2) */ +extern void _egg_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* VOID:OBJECT,LONG (eggmarshalers.list:3) */ +extern void _egg_marshal_VOID__OBJECT_LONG (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* ___egg_marshal_MARSHAL_H__ */ + diff --git a/systray/eggtraymanager.c b/systray/eggtraymanager.c new file mode 100644 index 0000000..74de1e8 --- /dev/null +++ b/systray/eggtraymanager.c @@ -0,0 +1,593 @@ +/* eggtraymanager.c + * Copyright (C) 2002 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include "eggtraymanager.h" +#include "eggmarshalers.h" + +/* Signals */ +enum +{ + TRAY_ICON_ADDED, + TRAY_ICON_REMOVED, + MESSAGE_SENT, + MESSAGE_CANCELLED, + LOST_SELECTION, + LAST_SIGNAL +}; + +typedef struct +{ + long id, len; + long remaining_len; + + long timeout; + Window window; + char *str; +} PendingMessage; + +static GObjectClass *parent_class = NULL; +static guint manager_signals[LAST_SIGNAL] = { 0 }; + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +static gboolean egg_tray_manager_check_running_xscreen (Screen *xscreen); + +static void egg_tray_manager_init (EggTrayManager *manager); +static void egg_tray_manager_class_init (EggTrayManagerClass *klass); + +static void egg_tray_manager_finalize (GObject *object); + +static void egg_tray_manager_unmanage (EggTrayManager *manager); + +GType +egg_tray_manager_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EggTrayManagerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) egg_tray_manager_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggTrayManager), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_tray_manager_init + }; + + our_type = g_type_register_static (G_TYPE_OBJECT, "EggTrayManager", &our_info, 0); + } + + return our_type; + +} + +static void +egg_tray_manager_init (EggTrayManager *manager) +{ + manager->socket_table = g_hash_table_new (NULL, NULL); +} + +static void +egg_tray_manager_class_init (EggTrayManagerClass *klass) +{ + GObjectClass *gobject_class; + + parent_class = g_type_class_peek_parent (klass); + gobject_class = (GObjectClass *)klass; + + gobject_class->finalize = egg_tray_manager_finalize; + + manager_signals[TRAY_ICON_ADDED] = + g_signal_new ("tray_icon_added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + + manager_signals[TRAY_ICON_REMOVED] = + g_signal_new ("tray_icon_removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + manager_signals[MESSAGE_SENT] = + g_signal_new ("message_sent", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, message_sent), + NULL, NULL, + _egg_marshal_VOID__OBJECT_STRING_LONG_LONG, + G_TYPE_NONE, 4, + GTK_TYPE_SOCKET, + G_TYPE_STRING, + G_TYPE_LONG, + G_TYPE_LONG); + manager_signals[MESSAGE_CANCELLED] = + g_signal_new ("message_cancelled", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, message_cancelled), + NULL, NULL, + _egg_marshal_VOID__OBJECT_LONG, + G_TYPE_NONE, 2, + GTK_TYPE_SOCKET, + G_TYPE_LONG); + manager_signals[LOST_SELECTION] = + g_signal_new ("lost_selection", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, lost_selection), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + +static void +egg_tray_manager_finalize (GObject *object) +{ + EggTrayManager *manager; + + manager = EGG_TRAY_MANAGER (object); + + egg_tray_manager_unmanage (manager); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +EggTrayManager * +egg_tray_manager_new (void) +{ + EggTrayManager *manager; + + manager = g_object_new (EGG_TYPE_TRAY_MANAGER, NULL); + + return manager; +} + +static gboolean +egg_tray_manager_plug_removed (GtkSocket *socket, + EggTrayManager *manager) +{ + Window *window; + + window = g_object_get_data (G_OBJECT (socket), "egg-tray-child-window"); + + g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window)); + g_object_set_data (G_OBJECT (socket), "egg-tray-child-window", + NULL); + + g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket); + + /* This destroys the socket. */ + return FALSE; +} + +static void +egg_tray_manager_handle_dock_request (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkWidget *socket; + Window *window; + + socket = gtk_socket_new (); + + /* We need to set the child window here + * so that the client can call _get functions + * in the signal handler + */ + window = g_new (Window, 1); + *window = xevent->data.l[2]; + + g_object_set_data_full (G_OBJECT (socket), + "egg-tray-child-window", + window, g_free); + g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, + socket); + + /* Add the socket only if it's been attached */ + if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket)))) + { + g_signal_connect (socket, "plug_removed", + G_CALLBACK (egg_tray_manager_plug_removed), manager); + + gtk_socket_add_id (GTK_SOCKET (socket), xevent->data.l[2]); + + g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2]), socket); + } + else + gtk_widget_destroy (socket); +} + +static void +pending_message_free (PendingMessage *message) +{ + g_free (message->str); + g_free (message); +} + +static void +egg_tray_manager_handle_message_data (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GList *p; + int len; + + /* Try to see if we can find the + * pending message in the list + */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window) + { + /* Append the message */ + len = MIN (msg->remaining_len, 20); + + memcpy ((msg->str + msg->len - msg->remaining_len), + &xevent->data, len); + msg->remaining_len -= len; + + if (msg->remaining_len == 0) + { + GtkSocket *socket; + + socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (msg->window)); + + if (socket) + { + g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, + socket, msg->str, msg->id, msg->timeout); + } + manager->messages = g_list_remove_link (manager->messages, + p); + + pending_message_free (msg); + } + + return; + } + } +} + +static void +egg_tray_manager_handle_begin_message (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GList *p; + PendingMessage *msg; + + /* Check if the same message is + * already in the queue and remove it if so + */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window && + xevent->data.l[4] == msg->id) + { + /* Hmm, we found it, now remove it */ + pending_message_free (msg); + manager->messages = g_list_remove_link (manager->messages, p); + break; + } + } + + /* Now add the new message to the queue */ + msg = g_new0 (PendingMessage, 1); + msg->window = xevent->window; + msg->timeout = xevent->data.l[2]; + msg->len = xevent->data.l[3]; + msg->id = xevent->data.l[4]; + msg->remaining_len = msg->len; + msg->str = g_malloc (msg->len + 1); + msg->str[msg->len] = '\0'; + manager->messages = g_list_prepend (manager->messages, msg); +} + +static void +egg_tray_manager_handle_cancel_message (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkSocket *socket; + + socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->window)); + + if (socket) + { + g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, + socket, xevent->data.l[2]); + } +} + +static GdkFilterReturn +egg_tray_manager_handle_event (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + switch (xevent->data.l[1]) + { + case SYSTEM_TRAY_REQUEST_DOCK: + egg_tray_manager_handle_dock_request (manager, xevent); + return GDK_FILTER_REMOVE; + + case SYSTEM_TRAY_BEGIN_MESSAGE: + egg_tray_manager_handle_begin_message (manager, xevent); + return GDK_FILTER_REMOVE; + + case SYSTEM_TRAY_CANCEL_MESSAGE: + egg_tray_manager_handle_cancel_message (manager, xevent); + return GDK_FILTER_REMOVE; + default: + break; + } + + return GDK_FILTER_CONTINUE; +} + +static GdkFilterReturn +egg_tray_manager_window_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) +{ + XEvent *xevent = (GdkXEvent *)xev; + EggTrayManager *manager = data; + + if (xevent->type == ClientMessage) + { + if (xevent->xclient.message_type == manager->opcode_atom) + { + return egg_tray_manager_handle_event (manager, (XClientMessageEvent *)xevent); + } + else if (xevent->xclient.message_type == manager->message_data_atom) + { + egg_tray_manager_handle_message_data (manager, (XClientMessageEvent *)xevent); + return GDK_FILTER_REMOVE; + } + } + else if (xevent->type == SelectionClear) + { + g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); + egg_tray_manager_unmanage (manager); + } + + return GDK_FILTER_CONTINUE; +} + +static void +egg_tray_manager_unmanage (EggTrayManager *manager) +{ + Display *display; + guint32 timestamp; + GtkWidget *invisible; + + if (manager->invisible == NULL) + return; + + invisible = manager->invisible; + g_assert (GTK_IS_INVISIBLE (invisible)); + g_assert (GTK_WIDGET_REALIZED (invisible)); + g_assert (GDK_IS_WINDOW (invisible->window)); + + display = GDK_WINDOW_XDISPLAY (invisible->window); + + if (XGetSelectionOwner (display, manager->selection_atom) == + GDK_WINDOW_XWINDOW (invisible->window)) + { + timestamp = gdk_x11_get_server_time (invisible->window); + XSetSelectionOwner (display, manager->selection_atom, None, timestamp); + } + + gdk_window_remove_filter (invisible->window, egg_tray_manager_window_filter, manager); + + manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ + gtk_widget_destroy (invisible); + g_object_unref (G_OBJECT (invisible)); +} + +static gboolean +egg_tray_manager_manage_xscreen (EggTrayManager *manager, Screen *xscreen) +{ + GtkWidget *invisible; + char *selection_atom_name; + guint32 timestamp; + GdkScreen *screen; + + g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + + /* If there's already a manager running on the screen + * we can't create another one. + */ +#if 0 + if (egg_tray_manager_check_running_xscreen (xscreen)) + return FALSE; +#endif + screen = gdk_display_get_screen (gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)), + XScreenNumberOfScreen (xscreen)); + + invisible = gtk_invisible_new_for_screen (screen); + gtk_widget_realize (invisible); + + gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); + + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + XScreenNumberOfScreen (xscreen)); + manager->selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); + + g_free (selection_atom_name); + + timestamp = gdk_x11_get_server_time (invisible->window); + XSetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom, + GDK_WINDOW_XWINDOW (invisible->window), timestamp); + + /* Check if we were could set the selection owner successfully */ + if (XGetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom) == + GDK_WINDOW_XWINDOW (invisible->window)) + { + XClientMessageEvent xev; + + xev.type = ClientMessage; + xev.window = RootWindowOfScreen (xscreen); + xev.message_type = XInternAtom (DisplayOfScreen (xscreen), "MANAGER", False); + + xev.format = 32; + xev.data.l[0] = timestamp; + xev.data.l[1] = manager->selection_atom; + xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window); + xev.data.l[3] = 0; /* manager specific data */ + xev.data.l[4] = 0; /* manager specific data */ + + XSendEvent (DisplayOfScreen (xscreen), + RootWindowOfScreen (xscreen), + False, StructureNotifyMask, (XEvent *)&xev); + + manager->invisible = invisible; + g_object_ref (G_OBJECT (manager->invisible)); + + manager->opcode_atom = XInternAtom (DisplayOfScreen (xscreen), + "_NET_SYSTEM_TRAY_OPCODE", + False); + + manager->message_data_atom = XInternAtom (DisplayOfScreen (xscreen), + "_NET_SYSTEM_TRAY_MESSAGE_DATA", + False); + + /* Add a window filter */ + gdk_window_add_filter (invisible->window, egg_tray_manager_window_filter, manager); + return TRUE; + } + else + { + gtk_widget_destroy (invisible); + + return FALSE; + } +} + +gboolean +egg_tray_manager_manage_screen (EggTrayManager *manager, + GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + + return egg_tray_manager_manage_xscreen (manager, + GDK_SCREEN_XSCREEN (screen)); +} + +static gboolean +egg_tray_manager_check_running_xscreen (Screen *xscreen) +{ + Atom selection_atom; + char *selection_atom_name; + + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + XScreenNumberOfScreen (xscreen)); + selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); + g_free (selection_atom_name); + + if (XGetSelectionOwner (DisplayOfScreen (xscreen), selection_atom)) + return TRUE; + else + return FALSE; +} + +gboolean +egg_tray_manager_check_running (GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + + return egg_tray_manager_check_running_xscreen (GDK_SCREEN_XSCREEN (screen)); +} + +char * +egg_tray_manager_get_child_title (EggTrayManager *manager, + EggTrayManagerChild *child) +{ + Window *child_window; + Atom utf8_string, atom, type; + int result; + char *retval; + int format; + gulong nitems; + gulong bytes_after; + guchar *val; + + g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), NULL); + g_return_val_if_fail (GTK_IS_SOCKET (child), NULL); + + child_window = g_object_get_data (G_OBJECT (child), + "egg-tray-child-window"); + + utf8_string = XInternAtom (GDK_DISPLAY (), "UTF8_STRING", False); + atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False); + + gdk_error_trap_push (); + + result = XGetWindowProperty (GDK_DISPLAY (), + *child_window, + atom, + 0, G_MAXLONG, + False, utf8_string, + &type, &format, &nitems, + &bytes_after, (guchar **)&val); + + if (gdk_error_trap_pop () || result != Success) + return NULL; + + if (type != utf8_string || + format != 8 || + nitems == 0) + { + if (val) + XFree (val); + return NULL; + } + + if (!g_utf8_validate (val, nitems, NULL)) + { + XFree (val); + return NULL; + } + + retval = g_strndup (val, nitems); + + XFree (val); + + return retval; + +} diff --git a/systray/eggtraymanager.h b/systray/eggtraymanager.h new file mode 100644 index 0000000..e2abdb6 --- /dev/null +++ b/systray/eggtraymanager.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtraymanager.h + * Copyright (C) 2002 Anders Carlsson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_TRAY_MANAGER_H__ +#define __EGG_TRAY_MANAGER_H__ + +#include +#include + +G_BEGIN_DECLS + +#define EGG_TYPE_TRAY_MANAGER (egg_tray_manager_get_type ()) +#define EGG_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManager)) +#define EGG_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) +#define EGG_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_MANAGER)) +#define EGG_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_MANAGER)) +#define EGG_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) + +typedef struct _EggTrayManager EggTrayManager; +typedef struct _EggTrayManagerClass EggTrayManagerClass; +typedef struct _EggTrayManagerChild EggTrayManagerChild; + +struct _EggTrayManager +{ + GObject parent_instance; + + Atom opcode_atom; + Atom selection_atom; + Atom message_data_atom; + + GtkWidget *invisible; + GdkScreen *screen; + + GList *messages; + GHashTable *socket_table; +}; + +struct _EggTrayManagerClass +{ + GObjectClass parent_class; + + void (* tray_icon_added) (EggTrayManager *manager, + EggTrayManagerChild *child); + void (* tray_icon_removed) (EggTrayManager *manager, + EggTrayManagerChild *child); + + void (* message_sent) (EggTrayManager *manager, + EggTrayManagerChild *child, + const gchar *message, + glong id, + glong timeout); + + void (* message_cancelled) (EggTrayManager *manager, + EggTrayManagerChild *child, + glong id); + + void (* lost_selection) (EggTrayManager *manager); +}; + +GType egg_tray_manager_get_type (void); + +gboolean egg_tray_manager_check_running (GdkScreen *screen); +EggTrayManager *egg_tray_manager_new (void); +gboolean egg_tray_manager_manage_screen (EggTrayManager *manager, + GdkScreen *screen); +char *egg_tray_manager_get_child_title (EggTrayManager *manager, + EggTrayManagerChild *child); + +G_END_DECLS + +#endif /* __EGG_TRAY_MANAGER_H__ */ diff --git a/systray/fixedtip.c b/systray/fixedtip.c new file mode 100644 index 0000000..b07ce04 --- /dev/null +++ b/systray/fixedtip.c @@ -0,0 +1,158 @@ +/* Metacity fixed tooltip routine */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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 Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "fixedtip.h" + +static GtkWidget *tip = NULL; +static GtkWidget *label = NULL; +static int screen_width = 0; +static int screen_height = 0; + +static gboolean +button_press_handler (GtkWidget *tip, + GdkEvent *event, + void *data) +{ + fixed_tip_hide (); + + return FALSE; +} + +static gboolean +expose_handler (GtkTooltips *tooltips) +{ + gtk_paint_flat_box (tip->style, tip->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, tip, "tooltip", + 0, 0, -1, -1); + + return FALSE; +} + +void +fixed_tip_show (int screen_number, + int root_x, int root_y, + gboolean strut_is_vertical, + int strut, + const char *markup_text) +{ + int w, h; + + if (tip == NULL) + { + tip = gtk_window_new (GTK_WINDOW_POPUP); +#ifdef HAVE_GTK_MULTIHEAD + { + GdkScreen *gdk_screen; + + gdk_screen = gdk_display_get_screen (gdk_get_default_display (), + screen_number); + gtk_window_set_screen (GTK_WINDOW (tip), + gdk_screen); + screen_width = gdk_screen_get_width (gdk_screen); + screen_height = gdk_screen_get_height (gdk_screen); + } +#else + screen_width = gdk_screen_width (); + screen_height = gdk_screen_height (); +#endif + + gtk_widget_set_app_paintable (tip, TRUE); + //gtk_window_set_policy (GTK_WINDOW (tip), FALSE, FALSE, TRUE); + gtk_window_set_resizable(GTK_WINDOW (tip), FALSE); + gtk_widget_set_name (tip, "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (tip), 4); + + g_signal_connect (G_OBJECT (tip), + "expose_event", + G_CALLBACK (expose_handler), + NULL); + + gtk_widget_add_events (tip, GDK_BUTTON_PRESS_MASK); + + g_signal_connect (G_OBJECT (tip), + "button_press_event", + G_CALLBACK (button_press_handler), + NULL); + + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (tip), label); + + g_signal_connect (G_OBJECT (tip), + "destroy", + G_CALLBACK (gtk_widget_destroyed), + &tip); + } + + gtk_label_set_markup (GTK_LABEL (label), markup_text); + + /* FIXME should also handle Xinerama here, just to be + * really cool + */ + gtk_window_get_size (GTK_WINDOW (tip), &w, &h); + + /* pad between panel and message window */ +#define PAD 5 + + if (strut_is_vertical) + { + if (strut > root_x) + root_x = strut + PAD; + else + root_x = strut - w - PAD; + + root_y -= h / 2; + } + else + { + if (strut > root_y) + root_y = strut + PAD; + else + root_y = strut - h - PAD; + + root_x -= w / 2; + } + + /* Push onscreen */ + if ((root_x + w) > screen_width) + root_x -= (root_x + w) - screen_width; + + if ((root_y + h) > screen_height) + root_y -= (root_y + h) - screen_height; + + gtk_window_move (GTK_WINDOW (tip), root_x, root_y); + + gtk_widget_show (tip); +} + +void +fixed_tip_hide (void) +{ + if (tip) + { + gtk_widget_destroy (tip); + tip = NULL; + } +} diff --git a/systray/fixedtip.h b/systray/fixedtip.h new file mode 100644 index 0000000..16ef500 --- /dev/null +++ b/systray/fixedtip.h @@ -0,0 +1,40 @@ +/* Fixed tooltip routine */ + +/* + * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc. + * + * 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 Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef FIXED_TIP_H +#define FIXED_TIP_H + +#include +#include + +/* root_x, root_y are where the speech balloon should be + * "pointing" and the strut is the panel edge we should be + * alongside. + */ +void fixed_tip_show (int screen_number, + int root_x, int root_y, + gboolean strut_is_vertical, + int strut, + const char *markup_text); +void fixed_tip_hide (void); + + +#endif diff --git a/systray/main.c b/systray/main.c new file mode 100644 index 0000000..dff5455 --- /dev/null +++ b/systray/main.c @@ -0,0 +1,149 @@ +#include +#include +#include + +#include +#include + +#include "panel.h" +#include "misc.h" +#include "plugin.h" + + + +#include "eggtraymanager.h" +#include "fixedtip.h" + + +//#define DEBUG +#include "dbg.h" + + +typedef struct { + GtkWidget *mainw; + plugin *plug; + GtkWidget *box; + ///// + EggTrayManager *tray_manager; + +} tray; + +//static void run_gtktray(tray *tr); + + + +static void +tray_added (EggTrayManager *manager, GtkWidget *icon, void *data) +{ + GtkWidget *box = (GtkWidget *)data; + + gtk_box_pack_end (GTK_BOX (box), icon, FALSE, FALSE, 0); + gtk_widget_show (icon); +} + +static void +tray_removed (EggTrayManager *manager, GtkWidget *icon, void *data) +{ + +} + +static void +message_sent (EggTrayManager *manager, GtkWidget *icon, const char *text, glong id, glong timeout, + void *data) +{ + /* FIXME multihead */ + int x, y; + + gdk_window_get_origin (icon->window, &x, &y); + + fixed_tip_show (0, x, y, FALSE, gdk_screen_height () - 50, text); +} + +static void +message_cancelled (EggTrayManager *manager, GtkWidget *icon, glong id, + void *data) +{ + +} + + + +static void +tray_destructor(plugin *p) +{ + tray *tr = (tray *)p->priv; + + ENTER; + /* Make sure we drop the manager selection */ + g_object_unref (G_OBJECT (tr->tray_manager)); + fixed_tip_hide (); + gtk_widget_destroy(tr->mainw); + g_free(tr); + RET(); +} + + + + +static int +tray_constructor(plugin *p) +{ + line s; + tray *tr; + GdkScreen *screen; + + ENTER; + s.len = 256; + while (get_line(p->fp, &s) != LINE_BLOCK_END) { + ERR( "image: illegal in this context %s\n", s.str); + RET(0); + } + + + tr = g_new0(tray, 1); + g_return_val_if_fail(tr != NULL, 0); + p->priv = tr; + tr->plug = p; + tr->mainw = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); + tr->box = p->panel->my_box_new(FALSE, 1); + gtk_container_add (GTK_CONTAINER (tr->mainw), tr->box); + gtk_container_add(GTK_CONTAINER(p->pwid), tr->mainw); + + screen = gtk_widget_get_screen (GTK_WIDGET (p->panel->topgwin)); + + if (egg_tray_manager_check_running(screen)) { + ERR("another systray already running\n"); + RET(1); + } + tr->tray_manager = egg_tray_manager_new (); + + if (!egg_tray_manager_manage_screen (tr->tray_manager, screen)) + g_printerr ("System tray didn't get the system tray manager selection\n"); + + g_signal_connect (tr->tray_manager, "tray_icon_added", + G_CALLBACK (tray_added), tr->box); + g_signal_connect (tr->tray_manager, "tray_icon_removed", + G_CALLBACK (tray_removed), tr->box); + g_signal_connect (tr->tray_manager, "message_sent", + G_CALLBACK (message_sent), tr->box); + g_signal_connect (tr->tray_manager, "message_cancelled", + G_CALLBACK (message_cancelled), tr->box); + + + RET(1); + +} + + +plugin_class tray_plugin_class = { + fname: NULL, + count: 0, + + type : "tray", + name : "tray", + version: "1.0", + description : "Old KDE/GNOME Tray", + + constructor : tray_constructor, + destructor : tray_destructor, +};