├── .gitignore ├── CHANGELOG ├── COPYING ├── CREDITS ├── INSTALL ├── Makefile ├── Makefile.common ├── README ├── TODO ├── bg.c ├── bg.h ├── configure ├── dbg.h ├── eggmarshalers.c ├── eggmarshalers.h ├── eggtraymanager.c ├── eggtraymanager.h ├── fixedtip.c ├── fixedtip.h ├── gdk-helper.c ├── gdk-helper.h ├── main.c ├── main.h ├── man └── trayer.1 ├── misc.c ├── misc.h ├── panel.c └── panel.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.config 2 | config.h 3 | *.o 4 | *.dep 5 | trayer 6 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | trayer changelog 2 | 3 | 1.1.7 4 | * Library cleanup 5 | * Icon Spacing 6 | 7 | 1.1.6 8 | * react on monitor setup changes 9 | 10 | 1.1.5 11 | * Replace deprecated functions from gdk 12 | * Fix panel width default value 13 | * Manual updates 14 | 15 | 1.1.4 16 | * Update cmdline help 17 | * Restore padding functionality 18 | * Add primary monitor cmdline flag 19 | 20 | 1.1.3 21 | * Minimal height restriction 22 | * Build process fixes 23 | 24 | 1.1.2 25 | * Type fixes. 26 | 27 | 1.1.1 28 | * Remove unusable features from legacy code 29 | * Replace hand written X event handling with gdk functions 30 | 31 | 1.1 32 | * multi monitor support 33 | * add distancefrom parameter 34 | * remove plugin handling code / code cleanup 35 | 36 | 1.0 37 | * tray code extracted from the main fbpanel sources 38 | * configuration using config file changed into command-line options 39 | * positionx and positiony options removed 40 | * distance option added 41 | 42 | ----------------------------------------------------------------------------- 43 | fbpanel changes follow 44 | ----------------------------------------------------------------------------- 45 | 3.8 46 | * warnings clean-up 47 | * X11 memory leacher was fixed 48 | * taskbar can be set to show only mapped/iconified and wins from other desktops 49 | * transparency initial support 50 | * gtkbar was ported to gtk2, so fbpanel is compiled with GTK_DISABLE_DEPRECETED 51 | * initial dll support 52 | 53 | 3.7 54 | * rounded corners (optional) 55 | * taskbar view fix 56 | 57 | 3.6 58 | * taskbar icon size fix 59 | * menu icon size fix 60 | * pager checks for drawable pixmap 61 | 62 | 3.5 63 | * Drag-n-Drop for launchbar 64 | * menu plugin 65 | * removed limith for max task size in taskbar 66 | 67 | 3.4 68 | * gtk2.2 linkage fix 69 | * strut fix 70 | * launchbar segfault on wrong config fix 71 | * '&' at the end of action var in launchbar config is depreciated 72 | 73 | 3.3 74 | * taskbar icon size fix 75 | 76 | 3.2 77 | * scroll mouse in pager changes desktops 78 | * packaging and makefiles now are ready for system wide install 79 | additionally ./configure was implemented 80 | * systray checks for another tray already running 81 | 82 | 3.1 83 | * improving icon quility in taskbar 84 | * system tray (aka notification area) support 85 | * NET_WM_STRUT_PARTIAL and NET_WM_STRUT were implmented 86 | * taskbar update icon image on every icon change 87 | 88 | 3.0 89 | * official version bump :-) 90 | 91 | 3.0-rc-1 92 | * porting to GTK2+. port is based on phako's patch 93 | "[ 678749 ] make it compile and work with gtk2" 94 | 95 | 96 | 2.2 97 | * support for XEmbed docklets via gtktray utility 98 | 99 | 2.1 100 | * tray plugin was written 101 | * documentation update 102 | * web site update 103 | 104 | 2.0 105 | * complete engine rewrite 106 | * new plugin API 107 | * pager fixes 108 | 109 | 1.4 110 | * bug-fixes for pager plugin 111 | 112 | 1.3 113 | * middle-click in taskbar will toggle shaded state of a window 114 | * added image plugin - this is simple plugin that just shows an image 115 | * pager eye-candy fixes 116 | * close_module function update 117 | 118 | 1.2 119 | * we've got new module - pager! Yeeaa-Haa!! 120 | * segfault on wrong config file was fixed 121 | 122 | 1.1 123 | * parsing engine was rewritten 124 | * modules' static variables were converted to mallocs 125 | * configurable size and postion of a panel 126 | * ability to specify what modules to load 127 | * '~' is accepted in config files 128 | * 129 | 130 | 131 | 1.0 132 | * 1.0-rc2 was released as 1.0 133 | 134 | 1.0-rc2 135 | * taskbar config file was added an option to switch tooltips on/off 136 | * added tooltips to taskbar (thanks to Joe MacDonald joe@deserted.net) 137 | 138 | 1.0-rc1 139 | * copyright comments were changed 140 | 141 | 1.0-rc0 142 | * added _NET_WM_STRUT support 143 | * panel now is unfocusable. this fixes iconify bug under sawfish 144 | * panel's height is calculated at run-time, instead of fixed 22 145 | 146 | 0.11 147 | * improved EWMH/NETWM support 148 | * added openbox support 149 | * added clock customization (thanks to Tooar tooar@gmx.net) 150 | * README was rewrited 151 | * bug fixes 152 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2005 Maciej "harnir" Delmanowski 2 | Copyright (C) 2002 Anatoly Asviyan (aka Arsen) 3 | Copyright (C) 2000 Peter Zelezny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Soft- 7 | ware"), to deal in the Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, publish, distribute, 9 | sublicense, and/or sell copies of the Software, and to permit persons to 10 | whom the Software is furnished to do so, subject to the following condi- 11 | tions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 18 | ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 21 | OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sargon/trayer-srg/b5693f5c6f554320d5a9bf328170f3194c1de2aa/CREDITS -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation: 2 | 3 | 1. Default way 4 | Most users (99.99%) should use this way :-) 5 | 6 | ./configure 7 | make 8 | su - 9 | make install 10 | 11 | 2. Litle customization 12 | Run ./configure --help to see supported options, then goto step 1 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Part 0 2 | # load common stuff 3 | TOPDIR = . 4 | include $(TOPDIR)/Makefile.common 5 | 6 | # Part 1 7 | # recursive make 8 | 9 | SRC = panel.c misc.c bg.c eggtraymanager.c eggmarshalers.c fixedtip.c main.c gdk-helper.c 10 | OBJ = $(SRC:%.c=%.o) 11 | DEP = $(SRC:%.c=%.dep) 12 | 13 | ifneq ($(MAKECMDGOALS),clean) 14 | ifneq ($(MAKECMDGOALS),distclean) 15 | -include $(DEP) 16 | endif 17 | endif 18 | 19 | 20 | TARGET = trayer 21 | $(TARGET): $(OBJ) 22 | $(CC) $(LDFLAGS) $(OBJ) -o $@ $(LIBS) 23 | ifeq (,$(DEVEL)) 24 | strip $@ 25 | endif 26 | 27 | all: $(TARGET) 28 | 29 | 30 | clean: 31 | $(RM) $(TARGET) $(OBJ) $(DEP) *~ 32 | 33 | distclean: 34 | rm -f Makefile.config config.h 35 | 36 | install: 37 | install -d $(DESTDIR)$(PREFIX)/bin 38 | install -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/bin 39 | 40 | uninstall: 41 | rm -f $(DESTDIR)$(PREFIX)/bin/$(TARGET) 42 | -------------------------------------------------------------------------------- /Makefile.common: -------------------------------------------------------------------------------- 1 | ifeq (,$(TOPDIR)) 2 | $(error TOPDIR variable must be defined) 3 | endif 4 | 5 | all: 6 | 7 | $(TOPDIR)/Makefile.config: 8 | $(error Please run $(TOPDIR)/configure first) 9 | 10 | ifneq ($(MAKECMDGOALS),clean) 11 | ifneq ($(MAKECMDGOALS),distclean) 12 | ifneq ($(MAKECMDGOALS),tar) 13 | -include $(TOPDIR)/Makefile.config 14 | endif 15 | endif 16 | endif 17 | 18 | CC ?= gcc 19 | PKG_CONFIG ?= pkg-config 20 | 21 | LIBS = $(shell $(PKG_CONFIG) --libs gtk+-2.0 gdk-pixbuf-2.0 x11) 22 | INCS = $(shell $(PKG_CONFIG) --cflags gtk+-2.0 gdk-pixbuf-2.0 x11) 23 | CFLAGS ?= -O2 -Wall 24 | ifneq (,$(DEVEL)) 25 | CFLAGS ?= -g -Wall 26 | endif 27 | 28 | %.o: %.c 29 | $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $< 30 | 31 | %.dep: %.c 32 | $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -MM $< -o $@ 33 | 34 | .PHONY: all clean distclean install uninstall 35 | 36 | distclean: clean 37 | install: all 38 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | trayer-srg 2 | 3 | NAME 4 | trayer-srg is a lightweight GTK2-based systray for UNIX desktop 5 | 6 | SYNOPSYS 7 | trayer [OPTION]... 8 | 9 | DESCRIPTION 10 | trayer-srg was forked from trayer in january 2010 to add some 11 | fancy features and clean up code. 12 | 13 | trayer is small program designed to provide systray functionality present 14 | in GNOME/KDE desktop enviroments for window managers which doesn't 15 | support that function. It's similar to other applications such as 16 | 'peksystray' and 'docker'. 17 | 18 | trayer code was extracted from fbpanel application, you can find more 19 | about it on it's homepage: http://fbpanel.sourceforge.net/ 20 | 21 | You can find new versions of trayer and support on FVWM-Crystal 22 | project homepage: http://fvwm-crystal.berlios.de/ 23 | 24 | You can find code of trayer-srg on his github page: 25 | http://github.com/sargon/trayer-srg 26 | 27 | OPTIONS 28 | -h - prints help message and exits 29 | -v - prints version and exits 30 | --edge - screen edge to use 31 | --align - alignment 32 | --margin - length of margin in pixels 33 | --distance - space between trayer's window and screen edge 34 | --distancefrom - Specifies which edge to calculate distance from, see above. 35 | --widthtype - how panel width is calculated: 36 | request - follow widgets' size requests. can shrink or grow dynamically 37 | pixel - ocupy fixed number of pixels, then 'width' variable holds a number 38 | percent - be 'width' precent of an edge 39 | --width - width of a panel (not used with --widthtype=request) 40 | --heighttype - how panel height is calcilated: 41 | pixel - ocupy fixed number of pixels, then 'height' variable 42 | holds a number 43 | --height - height of a panel in pixels 44 | --SetDockType - Identify panel window type as dock 45 | --SetPartialStrut - Reserve panel's space so that it will not be covered by 46 | maximazied windows 47 | --transparent - use transparency 48 | --tint - color used to "tint" background wallpaper with 49 | --alpha - percentage of transparency <0-256> 50 | --expand - specifies if trayer can accomodate extra space 51 | or not 52 | --padding - extra space between trayer's window frame and docked icons 53 | --monitor - define the mointor on which you like trayer to appear, 54 | number of zero to number of monitors minus one, 55 | or the string "primary". 56 | --iconspacing - Space between tray icons in pixels. 57 | 58 | TRAYER IN DISTROS 59 | 60 | debian 61 | https://packages.debian.org/source/jessie/trayer 62 | 63 | ubuntu 64 | http://packages.ubuntu.com/xenial/trayer 65 | 66 | archlinux 67 | https://aur.archlinux.org/packages/trayer-srg-git/ 68 | 69 | gentoo 70 | http://packages.gentoo.org/package/x11-misc/trayer-srg 71 | 72 | nixos 73 | http://hydra.nixos.org/job/nixpkgs/trunk/trayer.i686-linux/latest 74 | http://hydra.nixos.org/job/nixpkgs/trunk/trayer.x86_64-linux/latest 75 | 76 | AUTHORS 77 | Maciej Delmanowski 78 | Anatoly Asviyan - fbpanel 79 | Rafal Bisingier - conversion of configuration using ~/.fbpanel/* files to commandline options 80 | Grzegorz Niewęgłowski - code extraction from fbpanel 81 | Thomas Rydzynski - added new option 'distance' 82 | Keegan Carruthers-Smith - fix align 83 | Yury Akudovich - added new option distancefrom option 84 | Jens Peter Secher - various (debian trayer fork) 85 | Daniel Ehlers - multi monitor support, code cleanup 86 | Stu Black - help closing a problem with wrong values in _NET_WM_STRUT_PARTIAL 87 | Michael Weber - fix build problems, improvements cmdline parameter handling 88 | Johannes Bittner - primary switch for monitor selection 89 | Vladimir Murzin - fixing transparency startup problem 90 | Corey Richardson - Found some default value fuckup 91 | Constantine Verutin - React on changes in monitor setup. 92 | Robbie Harwood - Fixup build 93 | Omar Sandoval - Fixups 94 | Harri Nieminen - Fixup library linking 95 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | REDUCE 5 | ------ 6 | + clean up code 7 | 8 | 9 | FANCY 10 | ----- 11 | + background color selectable 12 | + review image resizing 13 | + (code) documentation 14 | - increase maintainbility 15 | + tray icon rendering 16 | -------------------------------------------------------------------------------- /bg.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "bg.h" 14 | #include "dbg.h" 15 | #include "config.h" 16 | 17 | static Display *dpy; 18 | //static int screen; 19 | static Window xroot; 20 | static Pixmap xrootbg = 0xc046ad; 21 | static GC gc; 22 | static Atom id = 0; 23 | 24 | static Pixmap 25 | bg_get_root_bg() 26 | { 27 | 28 | Pixmap ret; 29 | 30 | ENTER; 31 | ret = None; 32 | if (id) { 33 | int act_format, c = 2 ; 34 | u_long nitems ; 35 | u_long bytes_after ; 36 | u_char *prop ; 37 | Atom dummy_id; 38 | 39 | do { 40 | if (XGetWindowProperty(dpy, xroot, id, 0, 1, 41 | False, XA_PIXMAP, &dummy_id, &act_format, 42 | &nitems, &bytes_after, &prop) == Success) { 43 | if (prop) { 44 | ret = *((Pixmap *)prop); 45 | XFree(prop); 46 | break; 47 | } 48 | } 49 | } while (--c > 0); 50 | } 51 | //fprintf(stderr, "_XROOTPMAP_ID = 0x%x\n", ret); 52 | RET(ret); 53 | } 54 | 55 | GdkPixmap * 56 | bg_new_for_win(Window win) 57 | { 58 | Window dummy; 59 | Pixmap bgpix; 60 | GdkPixmap *gbgpix; 61 | unsigned int width, height ; 62 | unsigned int border, depth; 63 | int x, y; 64 | 65 | ENTER; 66 | XGetGeometry(dpy, win, &dummy, &x, &y, &width, &height, &border, &depth); 67 | XTranslateCoordinates(dpy, win, xroot, 0, 0, &x, &y, &dummy); 68 | gbgpix = gdk_pixmap_new(NULL, width, height, depth); 69 | if (!gbgpix) 70 | RET(NULL); 71 | bgpix = gdk_x11_drawable_get_xid(gbgpix); 72 | XSetTSOrigin(dpy, gc, -x, -y) ; 73 | XFillRectangle(dpy, bgpix, gc, 0, 0, width, height); 74 | RET(gbgpix); 75 | } 76 | 77 | void 78 | bg_init(Display *dpyn) 79 | { 80 | XGCValues gcv; 81 | uint mask; 82 | 83 | ENTER; 84 | dpy = dpyn; 85 | id = XInternAtom(dpy, "_XROOTPMAP_ID", False); 86 | xroot = DefaultRootWindow(dpy); 87 | xrootbg = bg_get_root_bg(); 88 | 89 | gcv.ts_x_origin = 0; 90 | gcv.ts_y_origin = 0; 91 | gcv.fill_style = FillTiled; 92 | mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle; 93 | if (xrootbg) { 94 | gcv.tile = xrootbg; 95 | mask |= GCTile ; 96 | } 97 | gc = XCreateGC (dpy, xroot, mask, &gcv) ; 98 | RET(); 99 | } 100 | 101 | void 102 | bg_rootbg_changed() 103 | { 104 | ENTER; 105 | xrootbg = bg_get_root_bg(); 106 | if (xrootbg != None) { 107 | XGCValues gcv; 108 | 109 | gcv.tile = xrootbg; 110 | XChangeGC(dpy, gc, GCTile, &gcv); 111 | DBG("changed\n"); 112 | } 113 | RET(); 114 | } 115 | 116 | void 117 | bg_close() 118 | { 119 | ENTER; 120 | XFreeGC(dpy, gc); 121 | RET(); 122 | } 123 | 124 | void 125 | modify_drawable(GdkDrawable *base, GdkGC *gc, guint32 tintcolor, gint alpha) 126 | { 127 | int w, h; 128 | GdkPixbuf *ret, *ret2; 129 | static GdkColormap *cmap = NULL; 130 | 131 | ENTER; 132 | gdk_drawable_get_size (base, &w, &h); 133 | if (!cmap) { 134 | cmap = gdk_colormap_get_system (); 135 | } 136 | ret = gdk_pixbuf_get_from_drawable (NULL, base, cmap, 0, 0, 0, 0, w, h); 137 | if (!ret) 138 | RET(); 139 | ret2 = gdk_pixbuf_composite_color_simple(ret, w, h, 140 | GDK_INTERP_HYPER, alpha, MIN(w, h), tintcolor, tintcolor); 141 | 142 | //gdk_pixbuf_render_to_drawable (ret2, base, gc, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0); 143 | gdk_draw_pixbuf (base, gc, ret2, 0, 0, 0, 0, w, h, GDK_RGB_DITHER_NONE, 0, 0); 144 | g_object_unref(ret); 145 | g_object_unref(ret2); 146 | RET(); 147 | } 148 | 149 | #ifdef TRANSPARENCY 150 | 151 | #include 152 | #include 153 | #include 154 | #include 155 | #include 156 | 157 | 158 | 159 | typedef struct _GdkWindowPaint GdkWindowPaint; 160 | 161 | struct _GdkWindowPaint 162 | { 163 | GdkRegion *region; 164 | GdkPixmap *pixmap; 165 | gint x_offset; 166 | gint y_offset; 167 | }; 168 | 169 | 170 | 171 | GdkGC * 172 | gdk_window_get_bg_gc (GdkWindow *window, 173 | GdkWindowPaint *paint) 174 | { 175 | GdkWindowObject *private = (GdkWindowObject *)window; 176 | guint gc_mask = 0; 177 | GdkGCValues gc_values; 178 | 179 | ENTER; 180 | if (private->bg_pixmap == GDK_PARENT_RELATIVE_BG && private->parent) { 181 | GdkWindowPaint tmp_paint = *paint; 182 | tmp_paint.x_offset += private->x; 183 | tmp_paint.y_offset += private->y; 184 | 185 | return gdk_window_get_bg_gc (GDK_WINDOW (private->parent), &tmp_paint); 186 | } else if (private->bg_pixmap && 187 | private->bg_pixmap != GDK_PARENT_RELATIVE_BG && 188 | private->bg_pixmap != GDK_NO_BG) { 189 | gc_values.fill = GDK_TILED; 190 | gc_values.tile = private->bg_pixmap; 191 | gc_values.ts_x_origin = -paint->x_offset; 192 | gc_values.ts_y_origin = -paint->y_offset; 193 | gc_mask = GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN; 194 | 195 | DBG("x_offset=%d y_offset=%d\n", paint->x_offset, paint->y_offset); 196 | return gdk_gc_new_with_values (paint->pixmap, &gc_values, gc_mask); 197 | } else { 198 | GdkGC *gc = _gdk_drawable_get_scratch_gc (paint->pixmap, FALSE); 199 | 200 | gdk_gc_set_foreground (gc, &(private->bg_color)); 201 | 202 | return g_object_ref (gc); 203 | } 204 | } 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /bg.h: -------------------------------------------------------------------------------- 1 | #ifndef _BG_H_ 2 | #define _BG_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void bg_init(); 13 | void bg_rootbg_changed(); 14 | GdkPixmap *bg_new_for_win(Window win); 15 | void modify_drawable(GdkDrawable *base, GdkGC *gc, guint32 tintcolor, gint alpha); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PREFIX=/usr 4 | 5 | if [ ! -z "$1" ]; then 6 | if [[ "$1" =~ ^--prefix= ]]; then 7 | PREFIX=$(echo $1 | cut -d= -f2) 8 | else 9 | echo "Usage: $0 [--prefix=]" 10 | exit 1 11 | fi 12 | fi 13 | 14 | if [ -f config.h ]; then 15 | echo "overwriting old config.h..." 16 | fi 17 | cat > config.h < Makefile.config < 2 | 3 | #define ERR(fmt, args...) fprintf(stderr, fmt, ## args) 4 | #define DBG2(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args) 5 | #define ENTER2 do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0) 6 | #define RET2(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\ 7 | return args; } while(0) 8 | //#define DBG 9 | #ifdef DBG 10 | 11 | #define ENTER do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0) 12 | #define RET(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\ 13 | return args; } while(0) 14 | #define DBG(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args) 15 | 16 | #else 17 | 18 | 19 | #define ENTER do { } while(0) 20 | #define RET(args...) return args; 21 | #define DBG(fmt, args...) do { } while(0) 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /eggmarshalers.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | 5 | #ifdef G_ENABLE_DEBUG 6 | #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) 7 | #define g_marshal_value_peek_char(v) g_value_get_char (v) 8 | #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) 9 | #define g_marshal_value_peek_int(v) g_value_get_int (v) 10 | #define g_marshal_value_peek_uint(v) g_value_get_uint (v) 11 | #define g_marshal_value_peek_long(v) g_value_get_long (v) 12 | #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) 13 | #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) 14 | #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) 15 | #define g_marshal_value_peek_enum(v) g_value_get_enum (v) 16 | #define g_marshal_value_peek_flags(v) g_value_get_flags (v) 17 | #define g_marshal_value_peek_float(v) g_value_get_float (v) 18 | #define g_marshal_value_peek_double(v) g_value_get_double (v) 19 | #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) 20 | #define g_marshal_value_peek_param(v) g_value_get_param (v) 21 | #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) 22 | #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) 23 | #define g_marshal_value_peek_object(v) g_value_get_object (v) 24 | #else /* !G_ENABLE_DEBUG */ 25 | /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. 26 | * Do not access GValues directly in your code. Instead, use the 27 | * g_value_get_*() functions 28 | */ 29 | #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int 30 | #define g_marshal_value_peek_char(v) (v)->data[0].v_int 31 | #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint 32 | #define g_marshal_value_peek_int(v) (v)->data[0].v_int 33 | #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint 34 | #define g_marshal_value_peek_long(v) (v)->data[0].v_long 35 | #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong 36 | #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 37 | #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 38 | #define g_marshal_value_peek_enum(v) (v)->data[0].v_int 39 | #define g_marshal_value_peek_flags(v) (v)->data[0].v_uint 40 | #define g_marshal_value_peek_float(v) (v)->data[0].v_float 41 | #define g_marshal_value_peek_double(v) (v)->data[0].v_double 42 | #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer 43 | #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer 44 | #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer 45 | #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer 46 | #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer 47 | #endif /* !G_ENABLE_DEBUG */ 48 | 49 | 50 | /* VOID:OBJECT,OBJECT (eggmarshalers.list:1) */ 51 | void 52 | _egg_marshal_VOID__OBJECT_OBJECT (GClosure *closure, 53 | GValue *return_value, 54 | guint n_param_values, 55 | const GValue *param_values, 56 | gpointer invocation_hint, 57 | gpointer marshal_data) 58 | { 59 | typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, 60 | gpointer arg_1, 61 | gpointer arg_2, 62 | gpointer data2); 63 | register GMarshalFunc_VOID__OBJECT_OBJECT callback; 64 | register GCClosure *cc = (GCClosure*) closure; 65 | register gpointer data1, data2; 66 | 67 | g_return_if_fail (n_param_values == 3); 68 | 69 | if (G_CCLOSURE_SWAP_DATA (closure)) 70 | { 71 | data1 = closure->data; 72 | data2 = g_value_peek_pointer (param_values + 0); 73 | } 74 | else 75 | { 76 | data1 = g_value_peek_pointer (param_values + 0); 77 | data2 = closure->data; 78 | } 79 | callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); 80 | 81 | callback (data1, 82 | g_marshal_value_peek_object (param_values + 1), 83 | g_marshal_value_peek_object (param_values + 2), 84 | data2); 85 | } 86 | 87 | /* VOID:OBJECT,STRING,LONG,LONG (eggmarshalers.list:2) */ 88 | void 89 | _egg_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, 90 | GValue *return_value, 91 | guint n_param_values, 92 | const GValue *param_values, 93 | gpointer invocation_hint, 94 | gpointer marshal_data) 95 | { 96 | typedef void (*GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (gpointer data1, 97 | gpointer arg_1, 98 | gpointer arg_2, 99 | glong arg_3, 100 | glong arg_4, 101 | gpointer data2); 102 | register GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG callback; 103 | register GCClosure *cc = (GCClosure*) closure; 104 | register gpointer data1, data2; 105 | 106 | g_return_if_fail (n_param_values == 5); 107 | 108 | if (G_CCLOSURE_SWAP_DATA (closure)) 109 | { 110 | data1 = closure->data; 111 | data2 = g_value_peek_pointer (param_values + 0); 112 | } 113 | else 114 | { 115 | data1 = g_value_peek_pointer (param_values + 0); 116 | data2 = closure->data; 117 | } 118 | callback = (GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (marshal_data ? marshal_data : cc->callback); 119 | 120 | callback (data1, 121 | g_marshal_value_peek_object (param_values + 1), 122 | g_marshal_value_peek_string (param_values + 2), 123 | g_marshal_value_peek_long (param_values + 3), 124 | g_marshal_value_peek_long (param_values + 4), 125 | data2); 126 | } 127 | 128 | /* VOID:OBJECT,LONG (eggmarshalers.list:3) */ 129 | void 130 | _egg_marshal_VOID__OBJECT_LONG (GClosure *closure, 131 | GValue *return_value, 132 | guint n_param_values, 133 | const GValue *param_values, 134 | gpointer invocation_hint, 135 | gpointer marshal_data) 136 | { 137 | typedef void (*GMarshalFunc_VOID__OBJECT_LONG) (gpointer data1, 138 | gpointer arg_1, 139 | glong arg_2, 140 | gpointer data2); 141 | register GMarshalFunc_VOID__OBJECT_LONG callback; 142 | register GCClosure *cc = (GCClosure*) closure; 143 | register gpointer data1, data2; 144 | 145 | g_return_if_fail (n_param_values == 3); 146 | 147 | if (G_CCLOSURE_SWAP_DATA (closure)) 148 | { 149 | data1 = closure->data; 150 | data2 = g_value_peek_pointer (param_values + 0); 151 | } 152 | else 153 | { 154 | data1 = g_value_peek_pointer (param_values + 0); 155 | data2 = closure->data; 156 | } 157 | callback = (GMarshalFunc_VOID__OBJECT_LONG) (marshal_data ? marshal_data : cc->callback); 158 | 159 | callback (data1, 160 | g_marshal_value_peek_object (param_values + 1), 161 | g_marshal_value_peek_long (param_values + 2), 162 | data2); 163 | } 164 | 165 | -------------------------------------------------------------------------------- /eggmarshalers.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* VOID:OBJECT,OBJECT (eggmarshalers.list:1) */ 4 | void _egg_marshal_VOID__OBJECT_OBJECT (GClosure *closure, 5 | GValue *return_value, 6 | guint n_param_values, 7 | const GValue *param_values, 8 | gpointer invocation_hint, 9 | gpointer marshal_data); 10 | 11 | /* VOID:OBJECT,STRING,LONG,LONG (eggmarshalers.list:2) */ 12 | void _egg_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, 13 | GValue *return_value, 14 | guint n_param_values, 15 | const GValue *param_values, 16 | gpointer invocation_hint, 17 | gpointer marshal_data); 18 | 19 | /* VOID:OBJECT,LONG (eggmarshalers.list:3) */ 20 | void _egg_marshal_VOID__OBJECT_LONG (GClosure *closure, 21 | GValue *return_value, 22 | guint n_param_values, 23 | const GValue *param_values, 24 | gpointer invocation_hint, 25 | gpointer marshal_data); 26 | -------------------------------------------------------------------------------- /eggtraymanager.c: -------------------------------------------------------------------------------- 1 | /* eggtraymanager.c 2 | * Copyright (C) 2002 Anders Carlsson 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "eggtraymanager.h" 26 | #include "eggmarshalers.h" 27 | #include "panel.h" 28 | #include "gdk-helper.h" 29 | 30 | /* Signals */ 31 | enum 32 | { 33 | TRAY_ICON_ADDED, 34 | TRAY_ICON_REMOVED, 35 | MESSAGE_SENT, 36 | MESSAGE_CANCELLED, 37 | LOST_SELECTION, 38 | LAST_SIGNAL 39 | }; 40 | 41 | typedef struct 42 | { 43 | long id, len; 44 | long remaining_len; 45 | 46 | long timeout; 47 | Window window; 48 | char *str; 49 | } PendingMessage; 50 | 51 | static GObjectClass *parent_class = NULL; 52 | static guint manager_signals[LAST_SIGNAL] = { 0 }; 53 | 54 | #define SYSTEM_TRAY_REQUEST_DOCK 0 55 | #define SYSTEM_TRAY_BEGIN_MESSAGE 1 56 | #define SYSTEM_TRAY_CANCEL_MESSAGE 2 57 | 58 | static gboolean egg_tray_manager_check_running_xscreen (Screen *xscreen); 59 | 60 | static void egg_tray_manager_init (EggTrayManager *manager); 61 | static void egg_tray_manager_class_init (EggTrayManagerClass *klass); 62 | 63 | static void egg_tray_manager_finalize (GObject *object); 64 | 65 | static void egg_tray_manager_unmanage (EggTrayManager *manager); 66 | 67 | GType 68 | egg_tray_manager_get_type (void) 69 | { 70 | static GType our_type = 0; 71 | 72 | if (our_type == 0) 73 | { 74 | static const GTypeInfo our_info = 75 | { 76 | sizeof (EggTrayManagerClass), 77 | (GBaseInitFunc) NULL, 78 | (GBaseFinalizeFunc) NULL, 79 | (GClassInitFunc) egg_tray_manager_class_init, 80 | NULL, /* class_finalize */ 81 | NULL, /* class_data */ 82 | sizeof (EggTrayManager), 83 | 0, /* n_preallocs */ 84 | (GInstanceInitFunc) egg_tray_manager_init 85 | }; 86 | 87 | our_type = g_type_register_static (G_TYPE_OBJECT, "EggTrayManager", &our_info, 0); 88 | } 89 | 90 | return our_type; 91 | 92 | } 93 | 94 | static void 95 | egg_tray_manager_init (EggTrayManager *manager) 96 | { 97 | manager->socket_table = g_hash_table_new (NULL, NULL); 98 | } 99 | 100 | static void 101 | egg_tray_manager_class_init (EggTrayManagerClass *klass) 102 | { 103 | GObjectClass *gobject_class; 104 | 105 | parent_class = g_type_class_peek_parent (klass); 106 | gobject_class = (GObjectClass *)klass; 107 | 108 | gobject_class->finalize = egg_tray_manager_finalize; 109 | 110 | manager_signals[TRAY_ICON_ADDED] = 111 | g_signal_new ("tray_icon_added", 112 | G_OBJECT_CLASS_TYPE (klass), 113 | G_SIGNAL_RUN_LAST, 114 | G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_added), 115 | NULL, NULL, 116 | g_cclosure_marshal_VOID__OBJECT, 117 | G_TYPE_NONE, 1, 118 | GTK_TYPE_SOCKET); 119 | 120 | manager_signals[TRAY_ICON_REMOVED] = 121 | g_signal_new ("tray_icon_removed", 122 | G_OBJECT_CLASS_TYPE (klass), 123 | G_SIGNAL_RUN_LAST, 124 | G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_removed), 125 | NULL, NULL, 126 | g_cclosure_marshal_VOID__OBJECT, 127 | G_TYPE_NONE, 1, 128 | GTK_TYPE_SOCKET); 129 | manager_signals[MESSAGE_SENT] = 130 | g_signal_new ("message_sent", 131 | G_OBJECT_CLASS_TYPE (klass), 132 | G_SIGNAL_RUN_LAST, 133 | G_STRUCT_OFFSET (EggTrayManagerClass, message_sent), 134 | NULL, NULL, 135 | _egg_marshal_VOID__OBJECT_STRING_LONG_LONG, 136 | G_TYPE_NONE, 4, 137 | GTK_TYPE_SOCKET, 138 | G_TYPE_STRING, 139 | G_TYPE_LONG, 140 | G_TYPE_LONG); 141 | manager_signals[MESSAGE_CANCELLED] = 142 | g_signal_new ("message_cancelled", 143 | G_OBJECT_CLASS_TYPE (klass), 144 | G_SIGNAL_RUN_LAST, 145 | G_STRUCT_OFFSET (EggTrayManagerClass, message_cancelled), 146 | NULL, NULL, 147 | _egg_marshal_VOID__OBJECT_LONG, 148 | G_TYPE_NONE, 2, 149 | GTK_TYPE_SOCKET, 150 | G_TYPE_LONG); 151 | manager_signals[LOST_SELECTION] = 152 | g_signal_new ("lost_selection", 153 | G_OBJECT_CLASS_TYPE (klass), 154 | G_SIGNAL_RUN_LAST, 155 | G_STRUCT_OFFSET (EggTrayManagerClass, lost_selection), 156 | NULL, NULL, 157 | g_cclosure_marshal_VOID__VOID, 158 | G_TYPE_NONE, 0); 159 | 160 | } 161 | 162 | static void 163 | egg_tray_manager_finalize (GObject *object) 164 | { 165 | EggTrayManager *manager; 166 | 167 | manager = EGG_TRAY_MANAGER (object); 168 | 169 | egg_tray_manager_unmanage (manager); 170 | 171 | G_OBJECT_CLASS (parent_class)->finalize (object); 172 | } 173 | 174 | EggTrayManager * 175 | egg_tray_manager_new (void) 176 | { 177 | EggTrayManager *manager; 178 | 179 | manager = g_object_new (EGG_TYPE_TRAY_MANAGER, NULL); 180 | 181 | return manager; 182 | } 183 | 184 | static gboolean 185 | egg_tray_manager_plug_removed (GtkSocket *socket, 186 | EggTrayManager *manager) 187 | { 188 | Window *window; 189 | 190 | window = g_object_get_data (G_OBJECT (socket), "egg-tray-child-window"); 191 | 192 | g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window)); 193 | g_object_set_data (G_OBJECT (socket), "egg-tray-child-window", 194 | NULL); 195 | 196 | g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket); 197 | 198 | /* This destroys the socket. */ 199 | return FALSE; 200 | } 201 | 202 | static void 203 | egg_tray_manager_handle_dock_request (EggTrayManager *manager, 204 | XClientMessageEvent *xevent) 205 | { 206 | GtkWidget *socket; 207 | Window *window; 208 | 209 | socket = gtk_socket_new (); 210 | 211 | /* We need to set the child window here 212 | * so that the client can call _get functions 213 | * in the signal handler 214 | */ 215 | window = g_new (Window, 1); 216 | *window = xevent->data.l[2]; 217 | 218 | g_object_set_data_full (G_OBJECT (socket), 219 | "egg-tray-child-window", 220 | window, g_free); 221 | g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, 222 | socket); 223 | 224 | /* Add the socket only if it's been attached */ 225 | if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket)))) 226 | { 227 | g_signal_connect (socket, "plug_removed", 228 | G_CALLBACK (egg_tray_manager_plug_removed), manager); 229 | 230 | gtk_socket_add_id (GTK_SOCKET (socket), xevent->data.l[2]); 231 | 232 | g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2]), socket); 233 | } 234 | else 235 | gtk_widget_destroy (socket); 236 | } 237 | 238 | static void 239 | pending_message_free (PendingMessage *message) 240 | { 241 | g_free (message->str); 242 | g_free (message); 243 | } 244 | 245 | static void 246 | egg_tray_manager_handle_message_data (EggTrayManager *manager, 247 | XClientMessageEvent *xevent) 248 | { 249 | GList *p; 250 | int len; 251 | 252 | /* Try to see if we can find the 253 | * pending message in the list 254 | */ 255 | for (p = manager->messages; p; p = p->next) 256 | { 257 | PendingMessage *msg = p->data; 258 | 259 | if (xevent->window == msg->window) 260 | { 261 | /* Append the message */ 262 | len = MIN (msg->remaining_len, 20); 263 | 264 | memcpy ((msg->str + msg->len - msg->remaining_len), 265 | &xevent->data, len); 266 | msg->remaining_len -= len; 267 | 268 | if (msg->remaining_len == 0) 269 | { 270 | GtkSocket *socket; 271 | 272 | socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (msg->window)); 273 | 274 | if (socket) 275 | { 276 | g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, 277 | socket, msg->str, msg->id, msg->timeout); 278 | } 279 | manager->messages = g_list_remove_link (manager->messages, 280 | p); 281 | 282 | pending_message_free (msg); 283 | } 284 | 285 | return; 286 | } 287 | } 288 | } 289 | 290 | static void 291 | egg_tray_manager_handle_begin_message (EggTrayManager *manager, 292 | XClientMessageEvent *xevent) 293 | { 294 | GList *p; 295 | PendingMessage *msg; 296 | 297 | /* Check if the same message is 298 | * already in the queue and remove it if so 299 | */ 300 | for (p = manager->messages; p; p = p->next) 301 | { 302 | PendingMessage *msg = p->data; 303 | 304 | if (xevent->window == msg->window && 305 | xevent->data.l[4] == msg->id) 306 | { 307 | /* Hmm, we found it, now remove it */ 308 | pending_message_free (msg); 309 | manager->messages = g_list_remove_link (manager->messages, p); 310 | break; 311 | } 312 | } 313 | 314 | /* Now add the new message to the queue */ 315 | msg = g_new0 (PendingMessage, 1); 316 | msg->window = xevent->window; 317 | msg->timeout = xevent->data.l[2]; 318 | msg->len = xevent->data.l[3]; 319 | msg->id = xevent->data.l[4]; 320 | msg->remaining_len = msg->len; 321 | msg->str = g_malloc (msg->len + 1); 322 | msg->str[msg->len] = '\0'; 323 | manager->messages = g_list_prepend (manager->messages, msg); 324 | } 325 | 326 | static void 327 | egg_tray_manager_handle_cancel_message (EggTrayManager *manager, 328 | XClientMessageEvent *xevent) 329 | { 330 | GtkSocket *socket; 331 | 332 | socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->window)); 333 | 334 | if (socket) 335 | { 336 | g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, 337 | socket, xevent->data.l[2]); 338 | } 339 | } 340 | 341 | static GdkFilterReturn 342 | egg_tray_manager_handle_event (EggTrayManager *manager, 343 | XClientMessageEvent *xevent) 344 | { 345 | switch (xevent->data.l[1]) 346 | { 347 | case SYSTEM_TRAY_REQUEST_DOCK: 348 | egg_tray_manager_handle_dock_request (manager, xevent); 349 | return GDK_FILTER_REMOVE; 350 | 351 | case SYSTEM_TRAY_BEGIN_MESSAGE: 352 | egg_tray_manager_handle_begin_message (manager, xevent); 353 | return GDK_FILTER_REMOVE; 354 | 355 | case SYSTEM_TRAY_CANCEL_MESSAGE: 356 | egg_tray_manager_handle_cancel_message (manager, xevent); 357 | return GDK_FILTER_REMOVE; 358 | default: 359 | break; 360 | } 361 | 362 | return GDK_FILTER_CONTINUE; 363 | } 364 | 365 | static GdkFilterReturn 366 | egg_tray_manager_window_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) 367 | { 368 | XEvent *xevent = (GdkXEvent *)xev; 369 | EggTrayManager *manager = data; 370 | 371 | if (xevent->type == ClientMessage) 372 | { 373 | if (xevent->xclient.message_type == manager->opcode_atom) 374 | { 375 | return egg_tray_manager_handle_event (manager, (XClientMessageEvent *)xevent); 376 | } 377 | else if (xevent->xclient.message_type == manager->message_data_atom) 378 | { 379 | egg_tray_manager_handle_message_data (manager, (XClientMessageEvent *)xevent); 380 | return GDK_FILTER_REMOVE; 381 | } 382 | } 383 | else if (xevent->type == SelectionClear) 384 | { 385 | g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); 386 | egg_tray_manager_unmanage (manager); 387 | } 388 | 389 | return GDK_FILTER_CONTINUE; 390 | } 391 | 392 | static void 393 | egg_tray_manager_unmanage (EggTrayManager *manager) 394 | { 395 | Display *display; 396 | guint32 timestamp; 397 | GtkWidget *invisible; 398 | 399 | if (manager->invisible == NULL) 400 | return; 401 | 402 | invisible = manager->invisible; 403 | g_assert (GTK_IS_INVISIBLE (invisible)); 404 | g_assert (GTK_WIDGET_REALIZED (invisible)); 405 | g_assert (GDK_IS_WINDOW (invisible->window)); 406 | 407 | display = GDK_WINDOW_XDISPLAY (invisible->window); 408 | 409 | if (XGetSelectionOwner (display, manager->selection_atom) == 410 | GDK_WINDOW_XWINDOW (invisible->window)) 411 | { 412 | timestamp = gdk_x11_get_server_time (invisible->window); 413 | XSetSelectionOwner (display, manager->selection_atom, None, timestamp); 414 | } 415 | 416 | gdk_window_remove_filter (invisible->window, egg_tray_manager_window_filter, manager); 417 | 418 | manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ 419 | gtk_widget_destroy (invisible); 420 | g_object_unref (G_OBJECT (invisible)); 421 | } 422 | 423 | static gboolean 424 | egg_tray_manager_manage_xscreen (EggTrayManager *manager, Screen *xscreen) 425 | { 426 | GtkWidget *invisible; 427 | char *selection_atom_name; 428 | guint32 timestamp; 429 | GdkScreen *screen; 430 | 431 | g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), FALSE); 432 | g_return_val_if_fail (manager->screen == NULL, FALSE); 433 | 434 | /* If there's already a manager running on the screen 435 | * we can't create another one. 436 | */ 437 | #if 0 438 | if (egg_tray_manager_check_running_xscreen (xscreen)) 439 | return FALSE; 440 | #endif 441 | screen = gdk_display_get_screen (gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)), 442 | XScreenNumberOfScreen (xscreen)); 443 | 444 | invisible = gtk_invisible_new_for_screen (screen); 445 | gtk_widget_realize (invisible); 446 | 447 | gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); 448 | 449 | selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 450 | XScreenNumberOfScreen (xscreen)); 451 | manager->selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); 452 | 453 | g_free (selection_atom_name); 454 | 455 | timestamp = gdk_x11_get_server_time (invisible->window); 456 | XSetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom, 457 | GDK_WINDOW_XWINDOW (invisible->window), timestamp); 458 | 459 | /* Check if we were could set the selection owner successfully */ 460 | if (XGetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom) == 461 | GDK_WINDOW_XWINDOW (invisible->window)) 462 | { 463 | XClientMessageEvent xev; 464 | 465 | xev.type = ClientMessage; 466 | xev.window = RootWindowOfScreen (xscreen); 467 | xev.message_type = XInternAtom (DisplayOfScreen (xscreen), "MANAGER", False); 468 | 469 | xev.format = 32; 470 | xev.data.l[0] = timestamp; 471 | xev.data.l[1] = manager->selection_atom; 472 | xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window); 473 | xev.data.l[3] = 0; /* manager specific data */ 474 | xev.data.l[4] = 0; /* manager specific data */ 475 | 476 | XSendEvent (DisplayOfScreen (xscreen), 477 | RootWindowOfScreen (xscreen), 478 | False, StructureNotifyMask, (XEvent *)&xev); 479 | 480 | manager->invisible = invisible; 481 | g_object_ref (G_OBJECT (manager->invisible)); 482 | 483 | manager->opcode_atom = XInternAtom (DisplayOfScreen (xscreen), 484 | "_NET_SYSTEM_TRAY_OPCODE", 485 | False); 486 | 487 | manager->message_data_atom = XInternAtom (DisplayOfScreen (xscreen), 488 | "_NET_SYSTEM_TRAY_MESSAGE_DATA", 489 | False); 490 | 491 | /* Add a window filter */ 492 | gdk_window_add_filter (invisible->window, egg_tray_manager_window_filter, manager); 493 | return TRUE; 494 | } 495 | else 496 | { 497 | gtk_widget_destroy (invisible); 498 | 499 | return FALSE; 500 | } 501 | } 502 | 503 | gboolean 504 | egg_tray_manager_manage_screen (EggTrayManager *manager, 505 | GdkScreen *screen) 506 | { 507 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 508 | g_return_val_if_fail (manager->screen == NULL, FALSE); 509 | 510 | return egg_tray_manager_manage_xscreen (manager, 511 | GDK_SCREEN_XSCREEN (screen)); 512 | } 513 | 514 | static gboolean 515 | egg_tray_manager_check_running_xscreen (Screen *xscreen) 516 | { 517 | Atom selection_atom; 518 | char *selection_atom_name; 519 | 520 | selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 521 | XScreenNumberOfScreen (xscreen)); 522 | selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); 523 | g_free (selection_atom_name); 524 | 525 | if (XGetSelectionOwner (DisplayOfScreen (xscreen), selection_atom)) 526 | return TRUE; 527 | else 528 | return FALSE; 529 | } 530 | 531 | gboolean 532 | egg_tray_manager_check_running (GdkScreen *screen) 533 | { 534 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 535 | 536 | return egg_tray_manager_check_running_xscreen (GDK_SCREEN_XSCREEN (screen)); 537 | } 538 | 539 | char * 540 | egg_tray_manager_get_child_title (EggTrayManager *manager, 541 | EggTrayManagerChild *child) 542 | { 543 | Window *child_window; 544 | Atom utf8_string, atom, type; 545 | int result; 546 | char *retval; 547 | int format; 548 | gulong nitems; 549 | gulong bytes_after; 550 | guchar *val; 551 | 552 | g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), NULL); 553 | g_return_val_if_fail (GTK_IS_SOCKET (child), NULL); 554 | 555 | child_window = g_object_get_data (G_OBJECT (child), 556 | "egg-tray-child-window"); 557 | 558 | utf8_string = XInternAtom (gdk_helper_display(), "UTF8_STRING", False); 559 | atom = XInternAtom (gdk_helper_display(), "_NET_WM_NAME", False); 560 | 561 | gdk_error_trap_push (); 562 | 563 | result = XGetWindowProperty (gdk_helper_display(), 564 | *child_window, 565 | atom, 566 | 0, G_MAXLONG, 567 | False, utf8_string, 568 | &type, &format, &nitems, 569 | &bytes_after, (guchar **)&val); 570 | 571 | if (gdk_error_trap_pop () || result != Success) 572 | return NULL; 573 | 574 | if (type != utf8_string || 575 | format != 8 || 576 | nitems == 0) 577 | { 578 | if (val) 579 | XFree (val); 580 | return NULL; 581 | } 582 | 583 | if (!g_utf8_validate ((const gchar*) val, nitems, NULL)) 584 | { 585 | XFree (val); 586 | return NULL; 587 | } 588 | 589 | retval = g_strndup ((const gchar*) val, nitems); 590 | 591 | XFree (val); 592 | 593 | return retval; 594 | 595 | } 596 | -------------------------------------------------------------------------------- /eggtraymanager.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 | /* eggtraymanager.h 3 | * Copyright (C) 2002 Anders Carlsson 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 | * Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #ifndef __EGG_TRAY_MANAGER_H__ 22 | #define __EGG_TRAY_MANAGER_H__ 23 | 24 | #include 25 | #include 26 | 27 | G_BEGIN_DECLS 28 | 29 | #define EGG_TYPE_TRAY_MANAGER (egg_tray_manager_get_type ()) 30 | #define EGG_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManager)) 31 | #define EGG_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) 32 | #define EGG_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_MANAGER)) 33 | #define EGG_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_MANAGER)) 34 | #define EGG_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) 35 | 36 | typedef struct _EggTrayManager EggTrayManager; 37 | typedef struct _EggTrayManagerClass EggTrayManagerClass; 38 | typedef struct _EggTrayManagerChild EggTrayManagerChild; 39 | 40 | struct _EggTrayManager 41 | { 42 | GObject parent_instance; 43 | 44 | Atom opcode_atom; 45 | Atom selection_atom; 46 | Atom message_data_atom; 47 | 48 | GtkWidget *invisible; 49 | GdkScreen *screen; 50 | 51 | GList *messages; 52 | GHashTable *socket_table; 53 | }; 54 | 55 | struct _EggTrayManagerClass 56 | { 57 | GObjectClass parent_class; 58 | 59 | void (* tray_icon_added) (EggTrayManager *manager, 60 | EggTrayManagerChild *child); 61 | void (* tray_icon_removed) (EggTrayManager *manager, 62 | EggTrayManagerChild *child); 63 | 64 | void (* message_sent) (EggTrayManager *manager, 65 | EggTrayManagerChild *child, 66 | const gchar *message, 67 | glong id, 68 | glong timeout); 69 | 70 | void (* message_cancelled) (EggTrayManager *manager, 71 | EggTrayManagerChild *child, 72 | glong id); 73 | 74 | void (* lost_selection) (EggTrayManager *manager); 75 | }; 76 | 77 | GType egg_tray_manager_get_type (void); 78 | 79 | gboolean egg_tray_manager_check_running (GdkScreen *screen); 80 | EggTrayManager *egg_tray_manager_new (void); 81 | gboolean egg_tray_manager_manage_screen (EggTrayManager *manager, 82 | GdkScreen *screen); 83 | char *egg_tray_manager_get_child_title (EggTrayManager *manager, 84 | EggTrayManagerChild *child); 85 | 86 | G_END_DECLS 87 | 88 | #endif /* __EGG_TRAY_MANAGER_H__ */ 89 | -------------------------------------------------------------------------------- /fixedtip.c: -------------------------------------------------------------------------------- 1 | /* Metacity fixed tooltip routine */ 2 | 3 | /* 4 | * Copyright (C) 2001 Havoc Pennington 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | * 02111-1307, USA. 20 | */ 21 | 22 | #include "fixedtip.h" 23 | 24 | static GtkWidget *tip = NULL; 25 | static GtkWidget *label = NULL; 26 | static int screen_width = 0; 27 | static int screen_height = 0; 28 | 29 | static gboolean 30 | button_press_handler (GtkWidget *tip, 31 | GdkEvent *event, 32 | void *data) 33 | { 34 | fixed_tip_hide (); 35 | 36 | return FALSE; 37 | } 38 | 39 | static gboolean 40 | expose_handler (GtkTooltips *tooltips) 41 | { 42 | gtk_paint_flat_box (tip->style, tip->window, 43 | GTK_STATE_NORMAL, GTK_SHADOW_OUT, 44 | NULL, tip, "tooltip", 45 | 0, 0, -1, -1); 46 | 47 | return FALSE; 48 | } 49 | 50 | void 51 | fixed_tip_show (int screen_number, 52 | int root_x, int root_y, 53 | gboolean strut_is_vertical, 54 | int strut, 55 | const char *markup_text) 56 | { 57 | int w, h; 58 | 59 | if (tip == NULL) 60 | { 61 | tip = gtk_window_new (GTK_WINDOW_POPUP); 62 | #ifdef HAVE_GTK_MULTIHEAD 63 | { 64 | GdkScreen *gdk_screen; 65 | 66 | gdk_screen = gdk_display_get_screen (gdk_get_default_display (), 67 | screen_number); 68 | gtk_window_set_screen (GTK_WINDOW (tip), 69 | gdk_screen); 70 | screen_width = gdk_screen_get_width (gdk_screen); 71 | screen_height = gdk_screen_get_height (gdk_screen); 72 | } 73 | #else 74 | screen_width = gdk_screen_width (); 75 | screen_height = gdk_screen_height (); 76 | #endif 77 | 78 | gtk_widget_set_app_paintable (tip, TRUE); 79 | //gtk_window_set_policy (GTK_WINDOW (tip), FALSE, FALSE, TRUE); 80 | gtk_window_set_resizable(GTK_WINDOW (tip), FALSE); 81 | gtk_widget_set_name (tip, "gtk-tooltips"); 82 | gtk_container_set_border_width (GTK_CONTAINER (tip), 4); 83 | 84 | g_signal_connect (G_OBJECT (tip), 85 | "expose_event", 86 | G_CALLBACK (expose_handler), 87 | NULL); 88 | 89 | gtk_widget_add_events (tip, GDK_BUTTON_PRESS_MASK); 90 | 91 | g_signal_connect (G_OBJECT (tip), 92 | "button_press_event", 93 | G_CALLBACK (button_press_handler), 94 | NULL); 95 | 96 | label = gtk_label_new (NULL); 97 | gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); 98 | gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); 99 | gtk_widget_show (label); 100 | 101 | gtk_container_add (GTK_CONTAINER (tip), label); 102 | 103 | g_signal_connect (G_OBJECT (tip), 104 | "destroy", 105 | G_CALLBACK (gtk_widget_destroyed), 106 | &tip); 107 | } 108 | 109 | gtk_label_set_markup (GTK_LABEL (label), markup_text); 110 | 111 | /* FIXME should also handle Xinerama here, just to be 112 | * really cool 113 | */ 114 | gtk_window_get_size (GTK_WINDOW (tip), &w, &h); 115 | 116 | /* pad between panel and message window */ 117 | #define PAD 5 118 | 119 | if (strut_is_vertical) 120 | { 121 | if (strut > root_x) 122 | root_x = strut + PAD; 123 | else 124 | root_x = strut - w - PAD; 125 | 126 | root_y -= h / 2; 127 | } 128 | else 129 | { 130 | if (strut > root_y) 131 | root_y = strut + PAD; 132 | else 133 | root_y = strut - h - PAD; 134 | 135 | root_x -= w / 2; 136 | } 137 | 138 | /* Push onscreen */ 139 | if ((root_x + w) > screen_width) 140 | root_x -= (root_x + w) - screen_width; 141 | 142 | if ((root_y + h) > screen_height) 143 | root_y -= (root_y + h) - screen_height; 144 | 145 | gtk_window_move (GTK_WINDOW (tip), root_x, root_y); 146 | 147 | gtk_widget_show (tip); 148 | } 149 | 150 | void 151 | fixed_tip_hide (void) 152 | { 153 | if (tip) 154 | { 155 | gtk_widget_destroy (tip); 156 | tip = NULL; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /fixedtip.h: -------------------------------------------------------------------------------- 1 | /* Fixed tooltip routine */ 2 | 3 | /* 4 | * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc. 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | * 02111-1307, USA. 20 | */ 21 | 22 | #ifndef FIXED_TIP_H 23 | #define FIXED_TIP_H 24 | 25 | #include 26 | #include 27 | 28 | /* root_x, root_y are where the speech balloon should be 29 | * "pointing" and the strut is the panel edge we should be 30 | * alongside. 31 | */ 32 | void fixed_tip_show (int screen_number, 33 | int root_x, int root_y, 34 | gboolean strut_is_vertical, 35 | int strut, 36 | const char *markup_text); 37 | void fixed_tip_hide (void); 38 | 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /gdk-helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gdk-helper.h" 4 | 5 | Display* gdk_helper_display() { 6 | return gdk_x11_display_get_xdisplay(gdk_display_get_default()); 7 | } 8 | -------------------------------------------------------------------------------- /gdk-helper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Display* gdk_helper_display(); 5 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "panel.h" 8 | #include "misc.h" 9 | 10 | #include "eggtraymanager.h" 11 | #include "fixedtip.h" 12 | #include "main.h" 13 | 14 | //#define DEBUG 15 | #include "dbg.h" 16 | 17 | 18 | typedef struct { 19 | GtkWidget *mainw; 20 | panel *panel; 21 | GtkWidget *box; 22 | ///// 23 | EggTrayManager *tray_manager; 24 | 25 | } tray; 26 | 27 | static void 28 | tray_added (EggTrayManager *manager, GtkWidget *icon, void *data) 29 | { 30 | GtkWidget *box = (GtkWidget *)data; 31 | 32 | gtk_box_pack_end (GTK_BOX (box), icon, FALSE, FALSE, 0); 33 | gtk_widget_show (icon); 34 | } 35 | 36 | static void 37 | tray_removed (EggTrayManager *manager, GtkWidget *icon, void *data) 38 | { 39 | 40 | } 41 | 42 | static void 43 | message_sent (EggTrayManager *manager, GtkWidget *icon, const char *text, glong id, glong timeout, 44 | void *data) 45 | { 46 | /* FIXME multihead */ 47 | int x, y; 48 | 49 | gdk_window_get_origin (icon->window, &x, &y); 50 | 51 | fixed_tip_show (0, x, y, FALSE, gdk_screen_height () - 50, text); 52 | } 53 | 54 | static void 55 | message_cancelled (EggTrayManager *manager, GtkWidget *icon, glong id, 56 | void *data) 57 | { 58 | 59 | } 60 | 61 | void 62 | tray_destructor(panel *p) 63 | { 64 | tray *tr = (tray *)p->priv; 65 | 66 | ENTER; 67 | /* Make sure we drop the manager selection */ 68 | g_object_unref (G_OBJECT (tr->tray_manager)); 69 | fixed_tip_hide (); 70 | gtk_widget_destroy(tr->mainw); 71 | g_free(tr); 72 | RET(); 73 | } 74 | 75 | int 76 | tray_constructor(panel *p) 77 | { 78 | tray *tr; 79 | GdkScreen *screen; 80 | 81 | ENTER; 82 | 83 | tr = g_new0(tray, 1); 84 | g_return_val_if_fail(tr != NULL, 0); 85 | p->priv = tr; 86 | tr->panel = p; 87 | tr->mainw = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); 88 | tr->box = p->my_box_new(FALSE, 1); 89 | gtk_box_set_spacing(GTK_BOX(tr->box), p->icon_spacing); 90 | gtk_container_add (GTK_CONTAINER (tr->mainw), tr->box); 91 | gtk_container_add(GTK_CONTAINER(p->box), tr->mainw); 92 | 93 | screen = gtk_widget_get_screen (GTK_WIDGET (p->topgwin)); 94 | 95 | if (egg_tray_manager_check_running(screen)) { 96 | ERR("another systray already running\n"); 97 | RET(1); 98 | } 99 | tr->tray_manager = egg_tray_manager_new (); 100 | 101 | if (!egg_tray_manager_manage_screen (tr->tray_manager, screen)) 102 | g_printerr ("System tray didn't get the system tray manager selection\n"); 103 | 104 | g_signal_connect (tr->tray_manager, "tray_icon_added", 105 | G_CALLBACK (tray_added), tr->box); 106 | g_signal_connect (tr->tray_manager, "tray_icon_removed", 107 | G_CALLBACK (tray_removed), tr->box); 108 | g_signal_connect (tr->tray_manager, "message_sent", 109 | G_CALLBACK (message_sent), tr->box); 110 | g_signal_connect (tr->tray_manager, "message_cancelled", 111 | G_CALLBACK (message_cancelled), tr->box); 112 | 113 | 114 | RET(1); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #include "panel.h" 2 | 3 | int tray_constructor(panel *p); 4 | void tray_destructor(panel *p); 5 | 6 | -------------------------------------------------------------------------------- /man/trayer.1: -------------------------------------------------------------------------------- 1 | .TH TRAYER-SRG 1 LOCAL 2 | .SH NAME 3 | trayer-srg - a lightweight GTK2-based systray for UNIX desktop 4 | .SH SYNOPSYS 5 | .B trayer 6 | .B "[ 7 | .I OPTIONS 8 | .B "] 9 | .SH DESCRIPTION 10 | trayer is small program designed to provide systray functionality present in GNOME/KDE desktop enviroments for window managers which doesn't support that function. It's similar to other applications such as 'peksystray' and 'docker'. 11 | 12 | trayer code was extracted from fbpanel application, you can find more about it on it's homepage: 13 | .IB http://fbpanel.sourceforge.net/ 14 | 15 | You can find new versions of trayer and support on FVWM-Crystal project homepage: 16 | .IB http://fvwm-crystal.berlios.de/ 17 | 18 | trayer-srg was forked from trayer in january 2010 to add some fancy features and clean up code. 19 | It contains all changes from above versions as far as known. 20 | Code of trayer-srg can be found on github: 21 | .IB http://github.com/sargon/trayer-srg 22 | .SH OPTIONS 23 | .TP 24 | .BR \-h 25 | prints help message and exits 26 | .TP 27 | .BR \-v 28 | prints version and exits 29 | .TP 30 | .BR \--edge " EDGE" 31 | Use 32 | .I EDGE 33 | for orientation. Possible values for 34 | .I EDGE 35 | are 36 | .BR left, 37 | .BR right, 38 | .BR top, 39 | .BR bottom 40 | or 41 | .BR none. 42 | The default value is 43 | .BR bottom. 44 | .TP 45 | .BR \--align " ALIGNMENT" 46 | Orientation of docked icons inside the trayer panel. Possible values are 47 | .BR left, 48 | .BR center, 49 | .BR right, 50 | or 51 | .BR none. 52 | The default value is 53 | .BR center. 54 | .TP 55 | .BR \--margin " NUM" 56 | Length of margin in pixels. The default value is 57 | .BR 0. 58 | .TP 59 | .BR \--distance " NUM[,NUM"] 60 | Space between trayer's window and screen edge. If only one value for two edges 61 | is given, the same value is applied to both edges. 62 | When set to 0 the option has no effect. 63 | The default value is 64 | .BR 0,0. 65 | .TP 66 | .BR \--distancefrom " EDGE[,EDGE"] 67 | Specifies which edges to calculate distance from, see 68 | .BR --edge. 69 | The default value is 70 | .BR top,none. 71 | When 72 | .BR --distance 73 | is 0 then this option has no effect. 74 | .TP 75 | .BR \--widthtype " TYPE" 76 | Determine how width is calculated. Possible values for 77 | .I TYPE 78 | are 79 | .BR pixel, 80 | .BR percent, 81 | .BR request 82 | or 83 | .BR none. 84 | The default value is 85 | .BR percent. 86 | .TP 87 | .BR \--width " NUM" 88 | Width of a panel. When 89 | .BR --widthtype=request 90 | this option has no effect. The default value is 91 | .BR 100. 92 | .TP 93 | .BR \--heighttype " TYPE" 94 | Determine how height is calculated. Possible values for 95 | .I TYPE 96 | are 97 | .BR pixel, 98 | .BR request 99 | or 100 | .BR none. 101 | 102 | The default value is 103 | .BR pixel. 104 | .TP 105 | .BR \--height " NUM" 106 | Height of a panel. When 107 | .BR --heigthtype=request 108 | this option has no effect. The default value is 109 | .BR 26. 110 | .TP 111 | .BR \--SetDockTpe " BOOL" 112 | Identify panel window type as dock. The default value is 113 | .BR true. 114 | .TP 115 | .BR \--SetPartialStrut " BOOL" 116 | Reserve panel's space so that it will not be covered by maximazied windows. The 117 | default value is 118 | .BR false. 119 | .TP 120 | .BR \--transparent " BOOL" 121 | Use transparency. Default value is 122 | .BR false. 123 | .TP 124 | .BR \--tint " NUM" 125 | Color used to "tint" background wallpaper with. 126 | .I NUM 127 | is 32bit width hexadecimal number. 128 | The default value is 129 | .BR 0xFFFFFFFF. 130 | .TP 131 | .BR \--alpha " NUM" 132 | Percentage of transparency. 133 | .I NUM 134 | should be a value between 0 and 256. The default value is 135 | .BR 127. 136 | .TP 137 | .BR \--expand " BOOL" 138 | Specifies if trayer can accomodate extra space or not . The default 139 | value is 140 | .BR true. 141 | .TP 142 | .BR \--padding " NUM" 143 | Number of extra pixel space between trayer's window frame and docked icons. The 144 | default value is 145 | .BR 0. 146 | .TP 147 | .BR \--monitor " NUM|STRING" 148 | Define the monitor on which you like trayer to appear, number of zero to number 149 | of monitors minus one, or the string "primary" are valid. The default value is 150 | .BR 0. 151 | .SH EXAMPLES 152 | .LP 153 | Place trayer to the top right edge of the screen and prevent other fullsize 154 | windows to overlay it: 155 | .RS 156 | .nf 157 | \f8trayer trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --transparent true\fP 158 | .fi 159 | .RE 160 | .SH BUGS 161 | Report bugs to the issue tracker: 162 | .I https://github.com/sargon/trayer-srg/issues 163 | .SH CONTRIBUTORS 164 | Following people are or has been involved in development of trayer-srg: 165 | 166 | .IP "Maciej Delmanowski " 167 | .IP "Anatoly Asviyan 168 | fbpanel 169 | .IP "Rafal Bisingier " 170 | conversion of configuration using ~/.fbpanel/* files to commandline options 171 | .IP "Grzegorz Niewęgłowski " 172 | code extraction from fbpanel 173 | .IP "Thomas Rydzynski" 174 | added new option 'distance' 175 | .IP "Keegan Carruthers-Smith " 176 | fix align 177 | .IP "Yury Akudovich" 178 | added new option distancefrom option 179 | .IP "Jens Peter Secher " 180 | various (debian trayer fork) 181 | .IP "Daniel Ehlers " 182 | multi monitor support, code cleanup and current maintainer. 183 | .IP "Stu Black" 184 | help closing a problem with wrong values in _NET_WM_STRUT_PARTIAL 185 | .IP "Michael Weber" 186 | fix build problems, improvements cmdline parameter handling 187 | .IP "Johannes Bittner" 188 | primary switch for monitor selection 189 | .IP "Vladimir Murzin " 190 | fixing transparency startup problem 191 | .IP "Corey Richardson " Found some default value fuckup 192 | .IP "Constantine Verutin" React on changes in monitor setup. 193 | .IP "Robbie Harwood" Fixup build 194 | .IP "Omar Sandoval" Fixups 195 | 196 | .SH AUTHOR 197 | This man page is written by Daniel Ehlers. 198 | -------------------------------------------------------------------------------- /misc.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "misc.h" 16 | #include "panel.h" 17 | #include "gdk-helper.h" 18 | 19 | //#define DEBUG 20 | #include "dbg.h" 21 | 22 | 23 | /* X11 data types */ 24 | Atom a_UTF8_STRING; 25 | Atom a_XROOTPMAP_ID; 26 | 27 | /* old WM spec */ 28 | Atom a_WM_STATE; 29 | Atom a_WM_CLASS; 30 | 31 | /* new NET spec */ 32 | Atom a_NET_WORKAREA; 33 | Atom a_NET_CLIENT_LIST; 34 | Atom a_NET_CLIENT_LIST_STACKING; 35 | Atom a_NET_NUMBER_OF_DESKTOPS; 36 | Atom a_NET_CURRENT_DESKTOP; 37 | Atom a_NET_DESKTOP_NAMES; 38 | Atom a_NET_ACTIVE_WINDOW; 39 | Atom a_NET_WM_DESKTOP; 40 | Atom a_NET_WM_STATE; 41 | Atom a_NET_WM_STATE_SKIP_TASKBAR; 42 | Atom a_NET_WM_STATE_SKIP_PAGER; 43 | Atom a_NET_WM_STATE_STICKY; 44 | Atom a_NET_WM_STATE_HIDDEN; 45 | Atom a_NET_WM_STATE_SHADED; 46 | Atom a_NET_WM_WINDOW_TYPE; 47 | Atom a_NET_WM_WINDOW_TYPE_DESKTOP; 48 | Atom a_NET_WM_WINDOW_TYPE_DOCK; 49 | Atom a_NET_WM_WINDOW_TYPE_TOOLBAR; 50 | Atom a_NET_WM_WINDOW_TYPE_MENU; 51 | Atom a_NET_WM_WINDOW_TYPE_UTILITY; 52 | Atom a_NET_WM_WINDOW_TYPE_SPLASH; 53 | Atom a_NET_WM_WINDOW_TYPE_DIALOG; 54 | Atom a_NET_WM_WINDOW_TYPE_NORMAL; 55 | Atom a_NET_WM_DESKTOP; 56 | Atom a_NET_WM_NAME; 57 | Atom a_NET_WM_STRUT; 58 | Atom a_NET_WM_STRUT_PARTIAL; 59 | Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR; 60 | 61 | pair allign_pair[] = { 62 | { ALLIGN_NONE, "none" }, 63 | { ALLIGN_LEFT, "left" }, 64 | { ALLIGN_RIGHT, "right" }, 65 | { ALLIGN_CENTER, "center"}, 66 | { 0, NULL }, 67 | }; 68 | 69 | pair edge_pair[] = { 70 | { EDGE_NONE, "none" }, 71 | { EDGE_LEFT, "left" }, 72 | { EDGE_RIGHT, "right" }, 73 | { EDGE_TOP, "top" }, 74 | { EDGE_BOTTOM, "bottom" }, 75 | { 0, NULL }, 76 | }; 77 | 78 | pair distancefrom_pair[] = { 79 | { DISTANCEFROM_NONE, "none" }, 80 | { DISTANCEFROM_LEFT, "left" }, 81 | { DISTANCEFROM_RIGHT, "right" }, 82 | { DISTANCEFROM_TOP, "top" }, 83 | { DISTANCEFROM_BOTTOM, "bottom" }, 84 | { 0, NULL }, 85 | }; 86 | 87 | pair width_pair[] = { 88 | { WIDTH_NONE, "none" }, 89 | { WIDTH_REQUEST, "request" }, 90 | { WIDTH_PIXEL, "pixel" }, 91 | { WIDTH_PERCENT, "percent" }, 92 | { 0, NULL }, 93 | }; 94 | 95 | pair height_pair[] = { 96 | { HEIGHT_NONE, "none" }, 97 | { HEIGHT_REQUEST, "request" }, 98 | { HEIGHT_PIXEL, "pixel" }, 99 | { 0, NULL }, 100 | }; 101 | 102 | pair bool_pair[] = { 103 | { 0, "false" }, 104 | { 1, "true" }, 105 | { 0, NULL }, 106 | }; 107 | pair pos_pair[] = { 108 | { POS_NONE, "none" }, 109 | { POS_START, "start" }, 110 | { POS_END, "end" }, 111 | { 0, NULL}, 112 | }; 113 | 114 | 115 | int 116 | str2num(pair *p, gchar *str, int defval) 117 | { 118 | ENTER; 119 | for (;p && p->str; p++) { 120 | if (!g_ascii_strcasecmp(str, p->str)) 121 | RET(p->num); 122 | } 123 | RET(defval); 124 | } 125 | 126 | gchar * 127 | num2str(pair *p, int num, gchar *defval) 128 | { 129 | ENTER; 130 | for (;p && p->str; p++) { 131 | if (num == p->num) 132 | RET(p->str); 133 | } 134 | RET(defval); 135 | } 136 | 137 | int 138 | get_line(FILE *fp, line *s) 139 | { 140 | gchar *tmp, *tmp2; 141 | 142 | ENTER; 143 | if (!fp) { 144 | s->type = LINE_NONE; 145 | RET(s->type); 146 | } 147 | s->type = LINE_NONE; 148 | while (fgets(s->str, s->len, fp)) { 149 | g_strstrip(s->str); 150 | 151 | if (s->str[0] == '#' || s->str[0] == 0) { 152 | continue; 153 | } 154 | DBG( ">> %s\n", s->str); 155 | if (!g_ascii_strcasecmp(s->str, "}")) { 156 | s->type = LINE_BLOCK_END; 157 | break; 158 | } 159 | 160 | s->t[0] = s->str; 161 | for (tmp = s->str; isalnum(*tmp); tmp++); 162 | for (tmp2 = tmp; isspace(*tmp2); tmp2++); 163 | if (*tmp2 == '=') { 164 | for (++tmp2; isspace(*tmp2); tmp2++); 165 | s->t[1] = tmp2; 166 | *tmp = 0; 167 | s->type = LINE_VAR; 168 | } else if (*tmp2 == '{') { 169 | *tmp = 0; 170 | s->type = LINE_BLOCK_START; 171 | } else { 172 | ERR( "parser: unknown token: '%c'\n", *tmp2); 173 | } 174 | break; 175 | } 176 | RET(s->type); 177 | 178 | } 179 | 180 | int 181 | get_line_as_is(FILE *fp, line *s) 182 | { 183 | gchar *tmp, *tmp2; 184 | 185 | ENTER; 186 | if (!fp) { 187 | s->type = LINE_NONE; 188 | RET(s->type); 189 | } 190 | s->type = LINE_NONE; 191 | while (fgets(s->str, s->len, fp)) { 192 | g_strstrip(s->str); 193 | if (s->str[0] == '#' || s->str[0] == 0) 194 | continue; 195 | DBG( ">> %s\n", s->str); 196 | if (!g_ascii_strcasecmp(s->str, "}")) { 197 | s->type = LINE_BLOCK_END; 198 | DBG( " : line_block_end\n"); 199 | break; 200 | } 201 | for (tmp = s->str; isalnum(*tmp); tmp++); 202 | for (tmp2 = tmp; isspace(*tmp2); tmp2++); 203 | if (*tmp2 == '=') { 204 | s->type = LINE_VAR; 205 | } else if (*tmp2 == '{') { 206 | s->type = LINE_BLOCK_START; 207 | } else { 208 | DBG( " : ? <%c>\n", *tmp2); 209 | } 210 | break; 211 | } 212 | RET(s->type); 213 | 214 | } 215 | 216 | void resolve_atoms() 217 | { 218 | ENTER; 219 | 220 | a_UTF8_STRING = XInternAtom(gdk_helper_display(), "UTF8_STRING", False); 221 | a_XROOTPMAP_ID = XInternAtom(gdk_helper_display(), "_XROOTPMAP_ID", False); 222 | a_WM_STATE = XInternAtom(gdk_helper_display(), "WM_STATE", False); 223 | a_WM_CLASS = XInternAtom(gdk_helper_display(), "WM_CLASS", False); 224 | a_NET_WORKAREA = XInternAtom(gdk_helper_display(), "_NET_WORKAREA", False); 225 | a_NET_CLIENT_LIST = XInternAtom(gdk_helper_display(), "_NET_CLIENT_LIST", False); 226 | a_NET_CLIENT_LIST_STACKING = XInternAtom(gdk_helper_display(), "_NET_CLIENT_LIST_STACKING", False); 227 | a_NET_NUMBER_OF_DESKTOPS = XInternAtom(gdk_helper_display(), "_NET_NUMBER_OF_DESKTOPS", False); 228 | a_NET_CURRENT_DESKTOP = XInternAtom(gdk_helper_display(), "_NET_CURRENT_DESKTOP", False); 229 | a_NET_DESKTOP_NAMES = XInternAtom(gdk_helper_display(), "_NET_DESKTOP_NAMES", False); 230 | a_NET_ACTIVE_WINDOW = XInternAtom(gdk_helper_display(), "_NET_ACTIVE_WINDOW", False); 231 | a_NET_WM_DESKTOP = XInternAtom(gdk_helper_display(), "_NET_WM_DESKTOP", False); 232 | a_NET_WM_STATE = XInternAtom(gdk_helper_display(), "_NET_WM_STATE", False); 233 | a_NET_WM_STATE_SKIP_TASKBAR = XInternAtom(gdk_helper_display(), "_NET_WM_STATE_SKIP_TASKBAR", False); 234 | a_NET_WM_STATE_SKIP_PAGER = XInternAtom(gdk_helper_display(), "_NET_WM_STATE_SKIP_PAGER", False); 235 | a_NET_WM_STATE_STICKY = XInternAtom(gdk_helper_display(), "_NET_WM_STATE_STICKY", False); 236 | a_NET_WM_STATE_HIDDEN = XInternAtom(gdk_helper_display(), "_NET_WM_STATE_HIDDEN", False); 237 | a_NET_WM_STATE_SHADED = XInternAtom(gdk_helper_display(), "_NET_WM_STATE_SHADED", False); 238 | a_NET_WM_WINDOW_TYPE = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE", False); 239 | 240 | a_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_DESKTOP", False); 241 | a_NET_WM_WINDOW_TYPE_DOCK = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_DOCK", False); 242 | a_NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_TOOLBAR", False); 243 | a_NET_WM_WINDOW_TYPE_MENU = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_MENU", False); 244 | a_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_UTILITY", False); 245 | a_NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_SPLASH", False); 246 | a_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_DIALOG", False); 247 | a_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(gdk_helper_display(), "_NET_WM_WINDOW_TYPE_NORMAL", False); 248 | a_NET_WM_DESKTOP = XInternAtom(gdk_helper_display(), "_NET_WM_DESKTOP", False); 249 | a_NET_WM_NAME = XInternAtom(gdk_helper_display(), "_NET_WM_NAME", False); 250 | a_NET_WM_STRUT = XInternAtom(gdk_helper_display(), "_NET_WM_STRUT", False); 251 | a_NET_WM_STRUT_PARTIAL = XInternAtom(gdk_helper_display(), "_NET_WM_STRUT_PARTIAL", False); 252 | a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR 253 | = XInternAtom(gdk_helper_display(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); 254 | 255 | RET(); 256 | } 257 | 258 | 259 | void 260 | Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4) 261 | { 262 | XClientMessageEvent xev; 263 | 264 | xev.type = ClientMessage; 265 | xev.window = win; 266 | xev.message_type = type; 267 | xev.format = 32; 268 | xev.data.l[0] = l0; 269 | xev.data.l[1] = l1; 270 | xev.data.l[2] = l2; 271 | xev.data.l[3] = l3; 272 | xev.data.l[4] = l4; 273 | XSendEvent(gdk_helper_display(), GDK_ROOT_WINDOW(), False, 274 | (SubstructureNotifyMask | SubstructureRedirectMask), 275 | (XEvent *) & xev); 276 | } 277 | 278 | void * 279 | get_xaproperty (Window win, Atom prop, Atom type, int *nitems) 280 | { 281 | Atom type_ret; 282 | int format_ret; 283 | unsigned long items_ret; 284 | unsigned long after_ret; 285 | unsigned char *prop_data; 286 | 287 | ENTER; 288 | prop_data = NULL; 289 | XGetWindowProperty (gdk_helper_display(), win, prop, 0, 0x7fffffff, False, 290 | type, &type_ret, &format_ret, &items_ret, 291 | &after_ret, &prop_data); 292 | if (nitems) 293 | *nitems = items_ret; 294 | RET(prop_data); 295 | } 296 | 297 | static char* 298 | text_property_to_utf8 (const XTextProperty *prop) 299 | { 300 | char **list; 301 | int count; 302 | char *retval; 303 | 304 | ENTER; 305 | list = NULL; 306 | count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding), 307 | prop->format, 308 | prop->value, 309 | prop->nitems, 310 | &list); 311 | 312 | DBG("count=%d\n", count); 313 | if (count == 0) 314 | return NULL; 315 | 316 | retval = list[0]; 317 | list[0] = g_strdup (""); /* something to free */ 318 | 319 | g_strfreev (list); 320 | 321 | RET(retval); 322 | } 323 | 324 | char * 325 | get_textproperty(Window win, Atom atom) 326 | { 327 | XTextProperty text_prop; 328 | char *retval; 329 | 330 | ENTER; 331 | if (XGetTextProperty(gdk_helper_display(), win, &text_prop, atom)) { 332 | DBG("format=%d enc=%d nitems=%d value=%s \n", 333 | text_prop.format, 334 | text_prop.encoding, 335 | text_prop.nitems, 336 | text_prop.value); 337 | retval = text_property_to_utf8 (&text_prop); 338 | if (text_prop.nitems > 0) 339 | XFree (text_prop.value); 340 | RET(retval); 341 | 342 | } 343 | RET(NULL); 344 | } 345 | 346 | 347 | int 348 | get_net_number_of_desktops() 349 | { 350 | int desknum; 351 | unsigned long *data; 352 | 353 | ENTER; 354 | data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS, 355 | XA_CARDINAL, 0); 356 | if (!data) 357 | RET(0); 358 | 359 | desknum = *data; 360 | XFree (data); 361 | RET(desknum); 362 | } 363 | 364 | 365 | int 366 | get_net_current_desktop () 367 | { 368 | int desk; 369 | unsigned long *data; 370 | 371 | ENTER; 372 | data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0); 373 | if (!data) 374 | RET(0); 375 | 376 | desk = *data; 377 | XFree (data); 378 | RET(desk); 379 | } 380 | 381 | int 382 | get_net_wm_desktop(Window win) 383 | { 384 | int desk = 0; 385 | unsigned long *data; 386 | 387 | ENTER; 388 | data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0); 389 | if (data) { 390 | desk = *data; 391 | XFree (data); 392 | } 393 | RET(desk); 394 | } 395 | 396 | void 397 | get_net_wm_state(Window win, net_wm_state *nws) 398 | { 399 | Atom *state; 400 | int num3; 401 | 402 | 403 | ENTER; 404 | memset(nws, 0, sizeof(*nws)); 405 | if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3))) 406 | RET(); 407 | 408 | DBG( "%x: netwm state = { ", (unsigned int)win); 409 | while (--num3 >= 0) { 410 | if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) { 411 | DBG("NET_WM_STATE_SKIP_PAGER "); 412 | nws->skip_pager = 1; 413 | } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) { 414 | DBG( "NET_WM_STATE_SKIP_TASKBAR "); 415 | nws->skip_taskbar = 1; 416 | } else if (state[num3] == a_NET_WM_STATE_STICKY) { 417 | DBG( "NET_WM_STATE_STICKY "); 418 | nws->sticky = 1; 419 | } else if (state[num3] == a_NET_WM_STATE_HIDDEN) { 420 | DBG( "NET_WM_STATE_HIDDEN "); 421 | nws->hidden = 1; 422 | } else if (state[num3] == a_NET_WM_STATE_SHADED) { 423 | DBG( "NET_WM_STATE_SHADED "); 424 | nws->shaded = 1; 425 | } else { 426 | DBG( "... "); 427 | } 428 | } 429 | XFree(state); 430 | DBG( "}\n"); 431 | RET(); 432 | } 433 | 434 | 435 | 436 | 437 | void 438 | get_net_wm_window_type(Window win, net_wm_window_type *nwwt) 439 | { 440 | Atom *state; 441 | int num3; 442 | 443 | 444 | ENTER; 445 | memset(nwwt, 0, sizeof(*nwwt)); 446 | if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3))) 447 | RET(); 448 | 449 | DBG( "%x: netwm state = { ", (unsigned int)win); 450 | while (--num3 >= 0) { 451 | if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) { 452 | DBG("NET_WM_WINDOW_TYPE_DESKTOP "); 453 | nwwt->desktop = 1; 454 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) { 455 | DBG( "NET_WM_WINDOW_TYPE_DOCK "); 456 | nwwt->dock = 1; 457 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) { 458 | DBG( "NET_WM_WINDOW_TYPE_TOOLBAR "); 459 | nwwt->toolbar = 1; 460 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) { 461 | DBG( "NET_WM_WINDOW_TYPE_MENU "); 462 | nwwt->menu = 1; 463 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) { 464 | DBG( "NET_WM_WINDOW_TYPE_UTILITY "); 465 | nwwt->utility = 1; 466 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) { 467 | DBG( "NET_WM_WINDOW_TYPE_SPLASH "); 468 | nwwt->splash = 1; 469 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) { 470 | DBG( "NET_WM_WINDOW_TYPE_DIALOG "); 471 | nwwt->dialog = 1; 472 | } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) { 473 | DBG( "NET_WM_WINDOW_TYPE_NORMAL "); 474 | nwwt->normal = 1; 475 | } else { 476 | DBG( "... "); 477 | } 478 | } 479 | XFree(state); 480 | DBG( "}\n"); 481 | RET(); 482 | } 483 | 484 | 485 | 486 | 487 | 488 | int 489 | get_wm_state (Window win) 490 | { 491 | unsigned long *data; 492 | int ret = 0; 493 | 494 | ENTER; 495 | data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0); 496 | if (data) { 497 | ret = data[0]; 498 | XFree (data); 499 | } 500 | RET(ret); 501 | } 502 | 503 | static void 504 | calculate_width(int scrw, int wtype, int allign, int margin, 505 | int *panw, int *x) 506 | { 507 | ENTER; 508 | DBG("scrw=%d\n", scrw); 509 | DBG("IN panw=%d\n", *panw); 510 | //scrw -= 2; 511 | if (wtype == WIDTH_PERCENT) { 512 | /* sanity check */ 513 | if (*panw > 100) 514 | *panw = 100; 515 | else if (*panw < 0) 516 | *panw = 1; 517 | *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0; 518 | } 519 | if (allign != ALLIGN_CENTER) { 520 | if (margin > scrw) { 521 | ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n", 522 | margin, scrw); 523 | margin = 0; 524 | } 525 | if (wtype == WIDTH_PERCENT) 526 | //*panw = MAX(scrw - margin, *panw); 527 | ; 528 | else 529 | *panw = MIN(scrw - margin, *panw); 530 | } 531 | DBG("OUT panw=%d\n", *panw); 532 | if (allign == ALLIGN_LEFT) 533 | *x += margin; 534 | else if (allign == ALLIGN_RIGHT) { 535 | *x += scrw - *panw - margin; 536 | if (*x < 0) 537 | *x = 0; 538 | } else if (allign == ALLIGN_CENTER) 539 | *x += (scrw - *panw) / 2; 540 | RET(); 541 | } 542 | 543 | 544 | void 545 | calculate_position(panel *np, int *distance,int *distancefrom) 546 | { 547 | int sswidth, ssheight, minx, miny, distancex=0, distancey=0, i; 548 | GdkScreen *screen; 549 | GdkDisplay *display; 550 | GdkRectangle *monitorGeometry; 551 | 552 | ENTER; 553 | 554 | display = gdk_display_get_default (); 555 | screen = gdk_display_get_default_screen(display); 556 | 557 | monitorGeometry = (GdkRectangle*) malloc(sizeof(GdkRectangle)); 558 | 559 | if (np->monitor >= gdk_screen_get_n_monitors(screen)) { 560 | np->monitor = 0; 561 | ERR("trayer: monitor parameter isn't valid, resetting value to 0\n"); 562 | } 563 | 564 | gdk_screen_get_monitor_geometry(screen,np->monitor,monitorGeometry); 565 | 566 | sswidth = monitorGeometry->width; 567 | ssheight = monitorGeometry->height; 568 | 569 | minx = monitorGeometry->x; 570 | miny = monitorGeometry->y; 571 | 572 | free(monitorGeometry); 573 | 574 | for (i = 0; i < DISTANCE_MAX_ARRAY_SIZE; i++) { 575 | switch (distancefrom[i]) { 576 | case DISTANCEFROM_TOP: 577 | distancey = distance[i]; 578 | break; 579 | case DISTANCEFROM_BOTTOM: 580 | distancey = -distance[i]; 581 | break; 582 | case DISTANCEFROM_LEFT: 583 | distancex = distance[i]; 584 | break; 585 | case DISTANCEFROM_RIGHT: 586 | distancex = -distance[i]; 587 | break; 588 | } 589 | } 590 | 591 | DBG("x value: %d\ty value: %d\n", distancex, distancey); 592 | 593 | if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) { 594 | np->aw = np->width; 595 | np->ax = minx + distancex; 596 | calculate_width(sswidth, np->widthtype, np->allign, np->margin, 597 | &np->aw, &np->ax); 598 | np->ah = np->height; 599 | np->ah = MIN(PANEL_HEIGHT_MAX, np->ah); 600 | np->ah = MAX(PANEL_HEIGHT_MIN, np->ah); 601 | np->ay = miny + ((np->edge == EDGE_TOP) ? distancey : (ssheight - np->ah - distancey)); 602 | 603 | } else { 604 | np->ah = np->width; 605 | np->ay = miny + distancey; 606 | calculate_width(ssheight, np->widthtype, np->allign, np->margin, 607 | &np->ah, &np->ay); 608 | np->aw = np->height; 609 | np->aw = MIN(PANEL_HEIGHT_MAX, np->aw); 610 | np->aw = MAX(PANEL_HEIGHT_MIN, np->aw); 611 | np->ax = minx + ((np->edge == EDGE_LEFT) ? distancex : (sswidth - np->aw - distancex)); 612 | } 613 | DBG("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah); 614 | RET(); 615 | } 616 | 617 | 618 | 619 | gchar * 620 | expand_tilda(gchar *file) 621 | { 622 | ENTER; 623 | RET((file[0] == '~') ? 624 | g_strdup_printf("%s%s", getenv("HOME"), file+1) 625 | : g_strdup(file)); 626 | 627 | } 628 | 629 | 630 | 631 | 632 | 633 | Window 634 | Select_Window(Display *dpy) 635 | { 636 | int status; 637 | Cursor cursor; 638 | XEvent event; 639 | Window target_win = None, root = RootWindow(dpy,DefaultScreen(dpy)); 640 | int buttons = 0; 641 | 642 | ENTER; 643 | /* Make the target cursor */ 644 | cursor = XCreateFontCursor(dpy, XC_crosshair); 645 | 646 | /* Grab the pointer using target cursor, letting it room all over */ 647 | status = XGrabPointer(dpy, root, False, 648 | ButtonPressMask|ButtonReleaseMask, GrabModeSync, 649 | GrabModeAsync, root, cursor, CurrentTime); 650 | if (status != GrabSuccess) { 651 | ERR("Can't grab the mouse."); 652 | RET(None); 653 | } 654 | /* Let the user select a window... */ 655 | while ((target_win == None) || (buttons != 0)) { 656 | /* allow one more event */ 657 | XAllowEvents(dpy, SyncPointer, CurrentTime); 658 | XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event); 659 | switch (event.type) { 660 | case ButtonPress: 661 | if (target_win == None) { 662 | target_win = event.xbutton.subwindow; /* window selected */ 663 | DBG("target win = 0x%x\n", target_win); 664 | if (target_win == None) target_win = root; 665 | } 666 | buttons++; 667 | break; 668 | case ButtonRelease: 669 | if (buttons > 0) /* there may have been some down before we started */ 670 | buttons--; 671 | break; 672 | } 673 | } 674 | 675 | XUngrabPointer(dpy, CurrentTime); /* Done with pointer */ 676 | RET(target_win); 677 | } 678 | 679 | 680 | /* 681 | * SuxPanel version 0.1 682 | * Copyright (c) 2003 Leandro Pereira 683 | * 684 | * This program may be distributed under the terms of GNU General 685 | * Public License version 2. You should have received a copy of the 686 | * license with this program; if not, please consult http://www.fsf.org/. 687 | * 688 | * This program comes with no warranty. Use at your own risk. 689 | * 690 | */ 691 | 692 | 693 | GtkWidget * 694 | gtk_image_new_from_file_scaled(const gchar *file, gint width, 695 | gint height) 696 | { 697 | GtkWidget *img; 698 | 699 | ENTER; 700 | if (g_file_test(file, G_FILE_TEST_EXISTS)) { 701 | GError *err = NULL; 702 | GdkPixbuf *pb; 703 | 704 | pb = gdk_pixbuf_new_from_file(file, &err); 705 | if (err || !pb) { 706 | g_error_free(err); 707 | } else { 708 | GdkPixbuf *pb_scaled; 709 | 710 | pb_scaled = gdk_pixbuf_scale_simple(pb, width, height, 711 | GDK_INTERP_BILINEAR); 712 | 713 | img = gtk_image_new_from_pixbuf(pb_scaled); 714 | 715 | g_object_unref(pb); 716 | g_object_unref(pb_scaled); 717 | 718 | RET(img); 719 | } 720 | } 721 | 722 | img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, 723 | GTK_ICON_SIZE_BUTTON); 724 | 725 | RET(img); 726 | } 727 | 728 | 729 | void 730 | get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name) 731 | { 732 | GtkWidget *b; 733 | //gint focus_width; 734 | //gint focus_pad; 735 | 736 | ENTER; 737 | b = gtk_button_new(); 738 | if (parent) 739 | gtk_container_add(parent, b); 740 | gtk_widget_set_name(GTK_WIDGET(b), name); 741 | GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS); 742 | GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT); 743 | gtk_container_set_border_width (GTK_CONTAINER (b), 0); 744 | 745 | gtk_widget_show(b); 746 | gtk_widget_size_request(b, req); 747 | 748 | gtk_widget_destroy(b); 749 | RET(); 750 | } 751 | -------------------------------------------------------------------------------- /misc.h: -------------------------------------------------------------------------------- 1 | #ifndef MISC_H 2 | #define MISC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "panel.h" 11 | 12 | enum { LINE_NONE, LINE_BLOCK_START, LINE_BLOCK_END, LINE_VAR }; 13 | 14 | typedef struct { 15 | int num, len, type; 16 | gchar str[256]; 17 | gchar *t[3]; 18 | } line; 19 | 20 | 21 | typedef struct { 22 | int num; 23 | gchar *str; 24 | } pair; 25 | 26 | extern pair allign_pair[]; 27 | extern pair edge_pair[]; 28 | extern pair distancefrom_pair[]; 29 | extern pair width_pair[]; 30 | extern pair height_pair[]; 31 | extern pair bool_pair[]; 32 | extern pair pos_pair[]; 33 | 34 | int str2num(pair *p, gchar *str, int defval); 35 | gchar *num2str(pair *p, int num, gchar *defval); 36 | int get_line(FILE *fp, line *s); 37 | int get_line_as_is(FILE *fp, line *s); 38 | 39 | void Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4); 40 | void *get_xaproperty (Window win, Atom prop, Atom type, int *nitems); 41 | char *get_textproperty(Window win, Atom prop); 42 | void resolve_atoms(); 43 | Window Select_Window(Display *dpy); 44 | int get_net_number_of_desktops(); 45 | int get_net_current_desktop (); 46 | int get_net_wm_desktop(Window win); 47 | int get_wm_state (Window win); 48 | void get_net_wm_state(Window win, net_wm_state *nws); 49 | void get_net_wm_window_type(Window win, net_wm_window_type *nwwt); 50 | 51 | void calculate_position(panel *np, int *distance,int *distancefrom); 52 | gchar *expand_tilda(gchar *file); 53 | GtkWidget *gtk_image_new_from_file_scaled(const gchar *file, gint width, gint height); 54 | void get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /panel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "panel.h" 14 | #include "misc.h" 15 | #include "bg.h" 16 | #include "main.h" 17 | #include "gdk-helper.h" 18 | 19 | #define VERSION "1.1.6" 20 | 21 | static gchar version[] = VERSION; 22 | int distance[DISTANCE_MAX_ARRAY_SIZE]={0, 0}; 23 | int distancefrom[DISTANCE_MAX_ARRAY_SIZE]={DISTANCEFROM_TOP, DISTANCEFROM_NONE}; 24 | int expand=1 , padding=0; 25 | 26 | 27 | //#define DEBUG 28 | #include "dbg.h" 29 | 30 | 31 | static panel *p; 32 | static gchar *transparent_rc = "style 'transparent-style'\n" 33 | "{\n" 34 | "bg_pixmap[NORMAL] = \"\"\n" 35 | "bg_pixmap[INSENSITIVE] = \"\"\n" 36 | "bg_pixmap[PRELIGHT] = \"\"\n" 37 | "bg_pixmap[SELECTED] = \"\"\n" 38 | "bg_pixmap[ACTIVE] = \"\"\n" 39 | "}\n" 40 | "class \"GtkEventBox\" style \"transparent-style\"\n" 41 | "class \"GtkSocket\" style \"transparent-style\"\n" 42 | "class \"GtkBar\" style \"transparent-style\"\n" 43 | "class \"GtkBox\" style \"transparent-style\"\n" 44 | "\n"; 45 | 46 | static void set_bg(GtkWidget *widget, panel *p); 47 | 48 | /**************************************************** 49 | * panel's handlers for WM events * 50 | ****************************************************/ 51 | /* 52 | static void 53 | panel_del_wm_strut(panel *p) 54 | { 55 | XDeleteProperty(gdk_helper_display(), p->topxwin, a_NET_WM_STRUT); 56 | XDeleteProperty(gdk_helper_display(), p->topxwin, a_NET_WM_STRUT_PARTIAL); 57 | } 58 | */ 59 | 60 | static void 61 | panel_set_wm_strut(panel *p) 62 | { 63 | unsigned long data[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; 64 | int i = 4; 65 | 66 | ENTER; 67 | if (!GTK_WIDGET_MAPPED (p->topgwin)) 68 | return; 69 | switch (p->edge) { 70 | case EDGE_LEFT: 71 | i = 0; 72 | data[i] = p->aw; 73 | data[4 + i*2] = p->ay; 74 | data[5 + i*2] = p->ay + p->ah - 1; 75 | break; 76 | case EDGE_RIGHT: 77 | i = 1; 78 | data[i] = p->aw; 79 | data[4 + i*2] = p->ay; 80 | data[5 + i*2] = p->ay + p->ah - 1; 81 | break; 82 | case EDGE_TOP: 83 | i = 2; 84 | data[i] = p->ah; 85 | data[4 + i*2] = p->ax; 86 | data[5 + i*2] = p->ax + p->aw - 1; 87 | break; 88 | case EDGE_BOTTOM: 89 | i = 3; 90 | data[i] = p->ah; 91 | data[4 + i*2] = p->ax; 92 | data[5 + i*2] = p->ax + p->aw - 1 ; 93 | break; 94 | default: 95 | ERR("wrong edge %d. strut won't be set\n", p->edge); 96 | RET(); 97 | } 98 | DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]); 99 | 100 | XChangeProperty(gdk_helper_display(), p->topxwin, a_NET_WM_STRUT_PARTIAL, 101 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12); 102 | /* old spec, for wms that do not support STRUT_PARTIAL */ 103 | XChangeProperty(gdk_helper_display(), p->topxwin, a_NET_WM_STRUT, 104 | XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4); 105 | 106 | RET(); 107 | } 108 | 109 | static GdkFilterReturn 110 | panel_wm_events(GdkXEvent *xevent, GdkEvent *event, panel *p) 111 | { 112 | Atom at; 113 | Window win; 114 | XEvent *ev = (XEvent *) xevent; 115 | 116 | ENTER; 117 | DBG("win = 0x%x\n", ev->xproperty.window); 118 | if ( ev->type != PropertyNotify ) 119 | RET(GDK_FILTER_CONTINUE); 120 | 121 | at = ev->xproperty.atom; 122 | win = ev->xproperty.window; 123 | if (win == GDK_ROOT_WINDOW()) { 124 | if (at == a_XROOTPMAP_ID) { 125 | bg_rootbg_changed(); 126 | set_bg(p->topgwin, p); 127 | gtk_widget_queue_draw(p->topgwin); 128 | DBG("a_XROOTPMAP_ID\n"); 129 | } 130 | } 131 | RET(GDK_FILTER_CONTINUE); 132 | } 133 | 134 | /**************************************************** 135 | * panel's handlers for GTK events * 136 | ****************************************************/ 137 | 138 | 139 | static gint 140 | panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data) 141 | { 142 | ENTER; 143 | RET(FALSE); 144 | } 145 | 146 | static gint 147 | panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data) 148 | { 149 | ENTER; 150 | // TODO need to cleanup 151 | gtk_main_quit(); 152 | RET(FALSE); 153 | } 154 | 155 | 156 | 157 | static gint 158 | panel_size_req(GtkWidget *widget, GtkRequisition *req, panel *p) 159 | { 160 | ENTER; 161 | DBG("IN req=(%d, %d)\n", req->width, req->height); 162 | if (p->widthtype == WIDTH_REQUEST) 163 | p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height; 164 | if (p->heighttype == HEIGHT_REQUEST) 165 | p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width; 166 | calculate_position(p, distance, distancefrom); 167 | req->width = p->aw; 168 | req->height = p->ah; 169 | DBG("OUT req=(%d, %d)\n", req->width, req->height); 170 | RET( TRUE ); 171 | } 172 | 173 | static gint 174 | panel_size_alloc(GtkWidget *widget, GtkAllocation *a, panel *p) 175 | { 176 | ENTER; 177 | DBG("new alloc: size (%d, %d). pos (%d, %d)\n", a->width, a->height, a->x, a->y); 178 | DBG("old alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay); 179 | if (p->widthtype == WIDTH_REQUEST) 180 | p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height; 181 | if (p->heighttype == HEIGHT_REQUEST) 182 | p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width; 183 | calculate_position(p, distance, distancefrom); 184 | DBG("pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay); 185 | if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) { 186 | DBG("actual coords eq to preffered. just returning\n"); 187 | RET(TRUE); 188 | } 189 | 190 | gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay); 191 | if (p->setstrut) 192 | panel_set_wm_strut(p); 193 | RET(TRUE); 194 | } 195 | 196 | 197 | 198 | 199 | /**************************************************** 200 | * panel creation * 201 | ****************************************************/ 202 | 203 | static void 204 | set_bg(GtkWidget *widget, panel *p) 205 | { 206 | ENTER; 207 | if (p->gtopbg) 208 | g_object_unref(p->gtopbg); 209 | p->gtopbg = bg_new_for_win(p->topxwin); 210 | 211 | modify_drawable(p->gtopbg, p->topgwin->style->black_gc, p->tintcolor, p->alpha); 212 | 213 | gdk_window_set_back_pixmap(p->topgwin->window, p->gtopbg, FALSE); 214 | gdk_window_clear(p->topgwin->window); 215 | gtk_widget_queue_draw_area (p->topgwin, 0, 0, 2000, 2000); 216 | RET(); 217 | } 218 | 219 | static void 220 | panel_style_set(GtkWidget *widget, GtkStyle *s, panel *p) 221 | { 222 | ENTER; 223 | 224 | gtk_rc_parse_string(transparent_rc); 225 | if (GTK_WIDGET_REALIZED(widget)) 226 | set_bg(widget, p); 227 | RET(); 228 | } 229 | 230 | static gboolean 231 | panel_configure_event(GtkWidget *widget, GdkEventConfigure *event, panel *p) 232 | { 233 | static gint x = 0, y = 0, width = 0, height = 0; 234 | 235 | ENTER; 236 | if (x == event->x && y == event->y 237 | && width == event->width && height == event->height) 238 | RET(FALSE); 239 | x = event->x; 240 | y = event->y; 241 | width = event->width; 242 | height = event->height; 243 | set_bg(widget, p); 244 | RET(FALSE); 245 | } 246 | 247 | static gboolean 248 | panel_monitors_changed(GdkScreen* s, panel* p) 249 | { 250 | ENTER; 251 | if ( p->on_primary ) { 252 | p->monitor = gdk_screen_get_primary_monitor(s); 253 | } 254 | calculate_position(p, distance, distancefrom); 255 | gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah); 256 | if (p->setstrut) 257 | panel_set_wm_strut(p); 258 | RET(TRUE); 259 | } 260 | 261 | void 262 | panel_start_gui(panel *p) 263 | { 264 | ENTER; 265 | //gtk_rc_parse_string(transparent_rc); 266 | p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); 267 | 268 | gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0); 269 | gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE); 270 | gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "trayer"); 271 | gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel"); 272 | gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_CENTER); 273 | g_signal_connect ( G_OBJECT(p->topgwin) , "delete-event" , G_CALLBACK(panel_delete_event) , p); 274 | g_signal_connect ( G_OBJECT(p->topgwin) , "destroy-event", G_CALLBACK(panel_destroy_event), p); 275 | g_signal_connect ( G_OBJECT (p->topgwin), "size-request" , G_CALLBACK(panel_size_req) , p); 276 | g_signal_connect ( G_OBJECT (p->topgwin), "size-allocate", G_CALLBACK(panel_size_alloc), p); 277 | 278 | if (p->transparent) { 279 | g_signal_connect (G_OBJECT (p->topgwin), "configure-event", G_CALLBACK(panel_configure_event), p); 280 | g_signal_connect (G_OBJECT (p->topgwin), "style-set", G_CALLBACK( panel_style_set), p); 281 | } 282 | 283 | GdkDisplay *display = gdk_display_get_default (); 284 | GdkScreen *screen = gdk_display_get_default_screen(display); 285 | g_signal_connect ( screen, "monitors-changed", G_CALLBACK(panel_monitors_changed), (gpointer)p ); 286 | 287 | if (p->on_primary) { 288 | p->monitor = gdk_screen_get_primary_monitor(screen); 289 | 290 | } 291 | 292 | gtk_widget_realize(p->topgwin); 293 | gdk_window_set_decorations(p->topgwin->window, 0); 294 | gtk_widget_set_app_paintable(p->topgwin, TRUE); 295 | 296 | p->lbox = p->my_box_new(FALSE, 0); 297 | gtk_container_set_border_width(GTK_CONTAINER(p->lbox), 0); 298 | gtk_container_add(GTK_CONTAINER(p->topgwin), p->lbox); 299 | gtk_widget_show(p->lbox); 300 | 301 | if (p->allign == ALLIGN_RIGHT) { 302 | GtkWidget * expander = p->my_box_new(FALSE, 0); 303 | gtk_box_pack_start(GTK_BOX(p->lbox), expander, TRUE, TRUE, 0); 304 | gtk_widget_show(expander); 305 | } 306 | 307 | p->box = p->my_box_new(FALSE, 1); 308 | gtk_container_set_border_width(GTK_CONTAINER(p->box), 1); 309 | gtk_box_pack_start(GTK_BOX(p->lbox), p->box, FALSE, TRUE, padding); 310 | gtk_widget_show(p->box); 311 | 312 | // get properties on topgwin 313 | p->topGdkWindow = gtk_widget_get_window(p->topgwin); 314 | p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window); 315 | 316 | bg_init(gdk_helper_display()); 317 | 318 | /* make our window unfocusable */ 319 | gdk_window_set_accept_focus(p->topGdkWindow,False); 320 | 321 | if (p->setdocktype) { 322 | gdk_window_set_type_hint(p->topGdkWindow,GDK_WINDOW_TYPE_HINT_DOCK); 323 | } 324 | 325 | Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0); 326 | 327 | /************************/ 328 | /* Window Mapping Point */ 329 | gtk_widget_show_all(p->topgwin); 330 | Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0); 331 | 332 | gdk_window_stick ( p->topGdkWindow); 333 | gdk_window_set_skip_pager_hint ( p->topGdkWindow, True ); 334 | gdk_window_set_skip_taskbar_hint ( p->topGdkWindow, True ); 335 | 336 | XSelectInput (gdk_helper_display(), GDK_ROOT_WINDOW(), PropertyChangeMask); 337 | XSelectInput (gdk_helper_display(), p->topxwin, PropertyChangeMask | FocusChangeMask | StructureNotifyMask); 338 | gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_wm_events, p); 339 | 340 | calculate_position(p, distance, distancefrom); 341 | gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah); 342 | if (p->setstrut) 343 | panel_set_wm_strut(p); 344 | if (p->lower) 345 | XLowerWindow(gdk_helper_display(), p->topxwin); 346 | 347 | RET(); 348 | } 349 | 350 | static int 351 | panel_parse_global(panel *p) 352 | { 353 | ENTER; 354 | p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM) 355 | ? ORIENT_HORIZ : ORIENT_VERT; 356 | if (p->orientation == ORIENT_HORIZ) { 357 | p->my_box_new = gtk_hbox_new; 358 | } else { 359 | p->my_box_new = gtk_vbox_new; 360 | } 361 | if (p->width < 0) 362 | p->width = 100; 363 | if (p->widthtype == WIDTH_PERCENT && p->width > 100) 364 | p->width = 100; 365 | p->heighttype = HEIGHT_PIXEL; 366 | if (p->heighttype == HEIGHT_PIXEL) { 367 | if (p->height < PANEL_HEIGHT_MIN) { 368 | ERR( "height is bound by %i pixels\n", PANEL_HEIGHT_MIN ); 369 | p->height = PANEL_HEIGHT_MIN; 370 | } else if (p->height > PANEL_HEIGHT_MAX) { 371 | ERR( "height is bound by %i pixels\n", PANEL_HEIGHT_MAX ); 372 | p->height = PANEL_HEIGHT_MAX; 373 | } 374 | } 375 | panel_start_gui(p); 376 | RET(1); 377 | } 378 | 379 | int 380 | panel_start(panel *p) 381 | { 382 | /* parse global section */ 383 | ENTER; 384 | 385 | if (!panel_parse_global(p)) 386 | RET(0); 387 | 388 | if (!tray_constructor(p)) 389 | RET(0); 390 | 391 | gtk_widget_show_all(p->topgwin); 392 | RET(1); 393 | } 394 | 395 | void panel_stop(panel *p) 396 | { 397 | ENTER; 398 | 399 | tray_destructor(p); 400 | XSelectInput (gdk_helper_display(), GDK_ROOT_WINDOW(), NoEventMask); 401 | gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_wm_events, p); 402 | gtk_widget_destroy(p->topgwin); 403 | RET(); 404 | } 405 | 406 | void 407 | usage() 408 | { 409 | ENTER; 410 | printf("trayer %s - lightweight GTK2+ systray for UNIX desktops\n", version); 411 | printf("Command line options:\n"); 412 | printf(" -h -- print this help and exit\n"); 413 | printf(" -v -- print version and exit\n"); 414 | printf(" -l -- lower the window on startup\n"); 415 | printf(" --edge (default:bottom) \n"); 416 | printf(" --align (default:center)\n"); 417 | printf(" --margin (default:0)\n"); 418 | printf(" --widthtype (default:percent)\n"); 419 | printf(" --width (default:%i)\n",PANEL_WIDTH_DEFAULT); 420 | printf(" --heighttype (default:pixel)\n"); 421 | printf(" --height (default:%i)\n",PANEL_HEIGHT_DEFAULT); 422 | printf(" --SetDockType (default:true)\n"); 423 | printf(" --SetPartialStrut (default:true)\n"); 424 | printf(" --transparent (default:false)\n"); 425 | printf(" --alpha (default:127)\n"); 426 | printf(" --tint (default:0xFFFFFFFF)\n"); 427 | printf(" --distance [,] (default:0,0)\n"); 428 | printf(" --distancefrom [,] (default:none,none) \n"); 429 | printf(" --expand (default:true)\n"); 430 | printf(" --padding (default:0)\n"); 431 | printf(" --monitor (default:0)\n"); 432 | printf(" --iconspacing (default:0)\n"); 433 | } 434 | 435 | void 436 | handle_error(Display * d, XErrorEvent * ev) 437 | { 438 | char buf[256]; 439 | 440 | ENTER; 441 | XGetErrorText(gdk_helper_display(), ev->error_code, buf, 256); 442 | ERR( "trayer : X error: %s\n", buf); 443 | RET(); 444 | } 445 | 446 | int 447 | main(int argc, char *argv[], char *env[]) 448 | { 449 | int i, j; 450 | char *token; 451 | 452 | ENTER; 453 | setlocale(LC_CTYPE, ""); 454 | gtk_init(&argc, &argv); 455 | XSetLocaleModifiers(""); 456 | XSetErrorHandler((XErrorHandler) handle_error); 457 | // resolve xatoms 458 | resolve_atoms(); 459 | 460 | p = g_new0(panel, 1); 461 | memset(p, 0, sizeof(panel)); 462 | p->allign = ALLIGN_CENTER; 463 | p->edge = EDGE_BOTTOM; 464 | p->widthtype = WIDTH_PERCENT; 465 | p->width = PANEL_WIDTH_DEFAULT; 466 | p->heighttype = HEIGHT_PIXEL; 467 | p->height = PANEL_HEIGHT_DEFAULT; 468 | p->setdocktype = 1; 469 | p->setstrut = 0; 470 | p->transparent = 0; 471 | p->icon_spacing = 0; 472 | p->alpha = 127; 473 | p->tintcolor = 0xFFFFFFFF; 474 | p->xtopbg = None; 475 | p->monitor = 0; 476 | p->margin = 0; 477 | p->on_primary = 0; 478 | 479 | for (i = 1; i < argc; i++) { 480 | if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { 481 | usage(); 482 | exit(0); 483 | } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) { 484 | printf("trayer %s\n", version); 485 | exit(0); 486 | } else if (!strcmp(argv[i], "-l")) { 487 | p->lower = 1; 488 | } else if (!strcmp(argv[i], "--edge")) { 489 | i++; 490 | if (i == argc) { 491 | ERR( "trayer: missing edge parameter value\n"); 492 | usage(); 493 | exit(1); 494 | } else { 495 | p->edge = str2num(edge_pair, argv[i], EDGE_NONE); 496 | } 497 | } else if (!strcmp(argv[i], "--align")) { 498 | i++; 499 | if (i == argc) { 500 | ERR( "trayer: missing align parameter value\n"); 501 | usage(); 502 | exit(1); 503 | } else { 504 | p->allign = str2num(allign_pair, argv[i], ALLIGN_NONE); 505 | } 506 | } else if (!strcmp(argv[i], "--margin")) { 507 | i++; 508 | if (i == argc) { 509 | ERR( "trayer: missing margin parameter value\n"); 510 | usage(); 511 | exit(1); 512 | } else { 513 | p->margin = atoi(argv[i]); 514 | } 515 | } else if (!strcmp(argv[i], "--widthtype")) { 516 | i++; 517 | if (i == argc) { 518 | ERR( "trayer: missing widthtype parameter value\n"); 519 | usage(); 520 | exit(1); 521 | } else { 522 | p->widthtype = str2num(width_pair, argv[i], WIDTH_NONE); 523 | } 524 | } else if (!strcmp(argv[i], "--width")) { 525 | i++; 526 | if (i == argc) { 527 | ERR( "trayer: missing width parameter value\n"); 528 | usage(); 529 | exit(1); 530 | } else { 531 | p->width = atoi(argv[i]); 532 | } 533 | } else if (!strcmp(argv[i], "--heighttype")) { 534 | i++; 535 | if (i == argc) { 536 | ERR( "trayer: missing heighttype parameter value\n"); 537 | usage(); 538 | exit(1); 539 | } else { 540 | p->heighttype = str2num(height_pair, argv[i], HEIGHT_NONE); 541 | } 542 | } else if (!strcmp(argv[i], "--height")) { 543 | i++; 544 | if (i == argc) { 545 | ERR( "trayer: missing height parameter value\n"); 546 | usage(); 547 | exit(1); 548 | } else { 549 | p->height = atoi(argv[i]); 550 | } 551 | } else if (!strcmp(argv[i], "--SetDockType")) { 552 | i++; 553 | if (i == argc) { 554 | ERR( "trayer: missing SetDockType parameter value\n"); 555 | usage(); 556 | exit(1); 557 | } else { 558 | p->setdocktype = str2num(bool_pair, argv[i], 0); 559 | } 560 | } else if (!strcmp(argv[i], "--SetPartialStrut")) { 561 | i++; 562 | if (i == argc) { 563 | ERR( "trayer: missing SetPartialStrut parameter value\n"); 564 | usage(); 565 | exit(1); 566 | } else { 567 | p->setstrut = str2num(bool_pair, argv[i], 0); 568 | } 569 | } else if (!strcmp(argv[i], "--transparent")) { 570 | i++; 571 | if (i == argc) { 572 | ERR( "trayer: missing transparent parameter value\n"); 573 | usage(); 574 | exit(1); 575 | } else { 576 | p->transparent = str2num(bool_pair, argv[i], 1); 577 | } 578 | } else if (!strcmp(argv[i], "--alpha")) { 579 | i++; 580 | if (i == argc) { 581 | ERR( "trayer: missing alpha parameter value\n"); 582 | usage(); 583 | exit(1); 584 | } else { 585 | p->alpha = atoi(argv[i]); 586 | } 587 | } else if (!strcmp(argv[i], "--tint")) { 588 | i++; 589 | if (i == argc) { 590 | ERR( "trayer: missing tint parameter value\n"); 591 | usage(); 592 | exit(1); 593 | } else { 594 | p->tintcolor = strtoul(argv[i], NULL, 0); 595 | } 596 | } else if (!strcmp(argv[i], "--distance")) { 597 | i++; 598 | if (i == argc) { 599 | ERR( "trayer: missing distance parameter value\n"); 600 | usage(); 601 | exit(1); 602 | } else { 603 | token = strtok(argv[i], ","); 604 | for (j = 0; j < DISTANCE_MAX_ARRAY_SIZE; j++) { 605 | if (token != NULL) { 606 | distance[j] = atoi(token); 607 | token = strtok(NULL, ","); 608 | } else 609 | break; 610 | } 611 | if (j < DISTANCE_MAX_ARRAY_SIZE) { 612 | for (j = j ; j < DISTANCE_MAX_ARRAY_SIZE; j++) 613 | distance[j] = distance[j-1]; 614 | } 615 | } 616 | } else if (!strcmp(argv[i], "--distancefrom")) { 617 | i++; 618 | if (i == argc) { 619 | ERR( "trayer: missing distancefrom parameter value\n"); 620 | usage(); 621 | exit(1); 622 | } else { 623 | token = strtok(argv[i], ","); 624 | for (j = 0; j < DISTANCE_MAX_ARRAY_SIZE; j++) { 625 | if (token != NULL) { 626 | distancefrom[j] = str2num(distancefrom_pair, token, DISTANCEFROM_NONE); 627 | token = strtok(NULL, ","); 628 | } else 629 | break; 630 | } 631 | } 632 | } else if (!strcmp(argv[i], "--expand")) { 633 | i++; 634 | if (i == argc) { 635 | ERR( "trayer: missing expand parameter value\n"); 636 | usage(); 637 | exit(1); 638 | } else { 639 | expand = str2num(bool_pair, argv[i], 1); 640 | } 641 | } else if (!strcmp(argv[i], "--padding")) { 642 | i++; 643 | if (i == argc) { 644 | ERR( "trayer: missing padding parameter value\n"); 645 | usage(); 646 | exit(1); 647 | } else { 648 | padding = atoi(argv[i]); 649 | } 650 | } else if (!strcmp(argv[i], "--monitor")) { 651 | i++; 652 | if (i == argc) { 653 | ERR( "trayer: missing monitor parameter value\n"); 654 | usage(); 655 | exit(1); 656 | } else { 657 | if (g_ascii_isdigit(argv[i][0])) { 658 | p->monitor = atoi(argv[i]); 659 | } else if (!strcmp(argv[i], "primary")) { 660 | p->on_primary = 1; 661 | } 662 | } 663 | } else if (!strcmp(argv[i], "--iconspacing")) { 664 | i++; 665 | if (i == argc) { 666 | ERR( "trayer: missing icon padding parameter value\n"); 667 | usage(); 668 | exit(1); 669 | } else { 670 | p->icon_spacing = atoi(argv[i]); 671 | } 672 | } else { 673 | printf("trayer: unknown option - %s\n", argv[i]); 674 | usage(); 675 | exit(1); 676 | } 677 | } 678 | g_return_val_if_fail (p != NULL, 1); 679 | if (!panel_start(p)) { 680 | ERR( "trayer: can't start panel\n"); 681 | exit(1); 682 | } 683 | gtk_main(); 684 | panel_stop(p); 685 | g_free(p); 686 | 687 | exit(0); 688 | } 689 | -------------------------------------------------------------------------------- /panel.h: -------------------------------------------------------------------------------- 1 | #ifndef PANEL_H 2 | #define PANEL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "config.h" 10 | 11 | enum { ALLIGN_NONE, ALLIGN_LEFT, ALLIGN_RIGHT, ALLIGN_CENTER }; 12 | enum { EDGE_NONE, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_BOTTOM }; 13 | enum { DISTANCEFROM_NONE, DISTANCEFROM_LEFT, DISTANCEFROM_RIGHT, DISTANCEFROM_TOP, DISTANCEFROM_BOTTOM }; 14 | enum { WIDTH_NONE, WIDTH_REQUEST, WIDTH_PIXEL, WIDTH_PERCENT }; 15 | enum { HEIGHT_NONE, HEIGHT_PIXEL, HEIGHT_REQUEST }; 16 | enum { ORIENT_NONE, ORIENT_VERT, ORIENT_HORIZ }; 17 | enum { POS_NONE, POS_START, POS_END }; 18 | 19 | #define PANEL_HEIGHT_DEFAULT 26 20 | #define PANEL_HEIGHT_MAX 200 21 | #define PANEL_HEIGHT_MIN 2 22 | #define PANEL_WIDTH_DEFAULT 100 23 | #define DISTANCE_MAX_ARRAY_SIZE 2 24 | 25 | typedef struct { 26 | 27 | GtkWidget *topgwin; /* main panel window */ 28 | GdkWindow* topGdkWindow; // and it parent gdk window 29 | Window topxwin; /* and it X window */ 30 | GtkWidget *lbox; /* primary layout box */ 31 | GtkWidget *box; /* box that contains all plugins */ 32 | GtkWidget *(*my_box_new) (gboolean, gint); 33 | Pixmap xtopbg; 34 | GdkPixmap *gtopbg; 35 | 36 | int alpha; 37 | guint32 tintcolor; 38 | 39 | int ax, ay, aw, ah; /* actual location and size of a panel */ 40 | int allign, edge, margin; 41 | int orientation; 42 | int widthtype, width; 43 | int heighttype, height; 44 | 45 | int self_destroy : 1; 46 | int setdocktype : 1; 47 | int setstrut : 1; 48 | int transparent : 1; 49 | int on_primary : 1; 50 | int monitor; 51 | int icon_spacing; 52 | 53 | // Import from plugin structure 54 | gpointer priv; 55 | int expand; 56 | int padding; 57 | int lower; 58 | } panel; 59 | 60 | 61 | typedef struct { 62 | unsigned int modal : 1; 63 | unsigned int sticky : 1; 64 | unsigned int maximized_vert : 1; 65 | unsigned int maximized_horz : 1; 66 | unsigned int shaded : 1; 67 | unsigned int skip_taskbar : 1; 68 | unsigned int skip_pager : 1; 69 | unsigned int hidden : 1; 70 | unsigned int fullscreen : 1; 71 | unsigned int above : 1; 72 | unsigned int below : 1; 73 | } net_wm_state; 74 | 75 | typedef struct { 76 | unsigned int desktop : 1; 77 | unsigned int dock : 1; 78 | unsigned int toolbar : 1; 79 | unsigned int menu : 1; 80 | unsigned int utility : 1; 81 | unsigned int splash : 1; 82 | unsigned int dialog : 1; 83 | unsigned int normal : 1; 84 | } net_wm_window_type; 85 | 86 | 87 | extern Atom a_UTF8_STRING; 88 | extern Atom a_XROOTPMAP_ID; 89 | 90 | extern Atom a_WM_STATE; 91 | extern Atom a_WM_CLASS; 92 | 93 | extern Atom a_NET_WORKAREA; 94 | extern Atom a_NET_CLIENT_LIST; 95 | extern Atom a_NET_CLIENT_LIST_STACKING; 96 | extern Atom a_NET_NUMBER_OF_DESKTOPS; 97 | extern Atom a_NET_CURRENT_DESKTOP; 98 | extern Atom a_NET_DESKTOP_NAMES; 99 | extern Atom a_NET_ACTIVE_WINDOW; 100 | extern Atom a_NET_WM_STATE; 101 | extern Atom a_NET_WM_STATE_SKIP_TASKBAR; 102 | extern Atom a_NET_WM_STATE_SKIP_PAGER; 103 | extern Atom a_NET_WM_STATE_STICKY; 104 | extern Atom a_NET_WM_STATE_HIDDEN; 105 | extern Atom a_NET_WM_STATE_SHADED; 106 | 107 | #define a_NET_WM_STATE_REMOVE 0 /* remove/unset property */ 108 | #define a_NET_WM_STATE_ADD 1 /* add/set property */ 109 | #define a_NET_WM_STATE_TOGGLE 2 /* toggle property */ 110 | 111 | extern Atom a_NET_WM_WINDOW_TYPE; 112 | extern Atom a_NET_WM_WINDOW_TYPE_DESKTOP; 113 | extern Atom a_NET_WM_WINDOW_TYPE_DOCK; 114 | extern Atom a_NET_WM_WINDOW_TYPE_TOOLBAR; 115 | extern Atom a_NET_WM_WINDOW_TYPE_MENU; 116 | extern Atom a_NET_WM_WINDOW_TYPE_UTILITY; 117 | extern Atom a_NET_WM_WINDOW_TYPE_SPLASH; 118 | extern Atom a_NET_WM_WINDOW_TYPE_DIALOG; 119 | extern Atom a_NET_WM_WINDOW_TYPE_NORMAL; 120 | 121 | extern Atom a_NET_WM_DESKTOP; 122 | extern Atom a_NET_WM_NAME; 123 | extern Atom a_NET_WM_STRUT; 124 | extern Atom a_NET_WM_STRUT_PARTIAL; 125 | 126 | extern Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR; 127 | 128 | #endif 129 | --------------------------------------------------------------------------------