From 706ef69bfaec118bb289d903214cae4509881131 Mon Sep 17 00:00:00 2001 From: Sanel Zukan Date: Wed, 26 Oct 2011 16:04:37 +0000 Subject: [PATCH] Importing systray applet. Not completed yet... --- ede-panel/Panel.cpp | 6 +- ede-panel/Panel.h | 6 +- ede-panel/applets/Jamfile | 1 + ede-panel/applets/system-tray/Jamfile | 13 ++ ede-panel/applets/system-tray/Tray.cpp | 184 +++++++++++++++++++ ede-panel/applets/system-tray/Tray.h | 36 ++++ ede-panel/applets/system-tray/TrayWindow.cpp | 58 ++++++ ede-panel/applets/system-tray/TrayWindow.h | 22 +++ ede-panel/applets/taskbar/Taskbar.cpp | 2 +- 9 files changed, 323 insertions(+), 5 deletions(-) create mode 100644 ede-panel/applets/system-tray/Jamfile create mode 100644 ede-panel/applets/system-tray/Tray.cpp create mode 100644 ede-panel/applets/system-tray/Tray.h create mode 100644 ede-panel/applets/system-tray/TrayWindow.cpp create mode 100644 ede-panel/applets/system-tray/TrayWindow.h diff --git a/ede-panel/Panel.cpp b/ede-panel/Panel.cpp index 56a7604..718d90d 100644 --- a/ede-panel/Panel.cpp +++ b/ede-panel/Panel.cpp @@ -414,9 +414,11 @@ void Panel::load_applets(void) { "keyboard_layout.so", "cpu_monitor.so", "mem_monitor.so", + "system_tray.so", 0 }; +#if 0 String dir = Resource::find_data("panel-applets"); if(dir.empty()) return; @@ -428,7 +430,7 @@ void Panel::load_applets(void) { } mgr.fill_group(this); -#if 0 +#endif mgr.load("./applets/start-menu/start_menu.so"); mgr.load("./applets/quick-launch/quick_launch.so"); mgr.load("./applets/pager/pager.so"); @@ -437,6 +439,6 @@ void Panel::load_applets(void) { mgr.load("./applets/keyboard-layout/keyboard_layout.so"); mgr.load("./applets/cpu-monitor/cpu_monitor.so"); mgr.load("./applets/mem-monitor/mem_monitor.so"); + mgr.load("./applets/system-tray/system_tray.so"); mgr.fill_group(this); -#endif } diff --git a/ede-panel/Panel.h b/ede-panel/Panel.h index 5e69c28..f42bce5 100644 --- a/ede-panel/Panel.h +++ b/ede-panel/Panel.h @@ -4,8 +4,8 @@ #include #include "AppletManager.h" -#define EDE_PANEL_CAST_TO_PANEL(obj) ((Panel*)(obj)) -#define EDE_PANEL_GET_PANEL_OBJECT (EDE_PANEL_CAST_TO_PANEL(parent())) +#define EDE_PANEL_CAST_TO_PANEL(obj) ((Panel*)(obj)) +#define EDE_PANEL_GET_PANEL_OBJECT(w) (EDE_PANEL_CAST_TO_PANEL(w->parent())) EDELIB_NS_USING_AS(Window, PanelWindow) @@ -38,6 +38,8 @@ public: int panel_w(void) { return w(); } int panel_h(void) { return h(); } + + void relayout(void) { do_layout(); } }; #endif diff --git a/ede-panel/applets/Jamfile b/ede-panel/applets/Jamfile index b4d2be3..07cdb8f 100644 --- a/ede-panel/applets/Jamfile +++ b/ede-panel/applets/Jamfile @@ -18,4 +18,5 @@ SubInclude TOP ede-panel applets keyboard-layout ; SubInclude TOP ede-panel applets pager ; SubInclude TOP ede-panel applets quick-launch ; SubInclude TOP ede-panel applets start-menu ; +SubInclude TOP ede-panel applets system-tray ; SubInclude TOP ede-panel applets taskbar ; diff --git a/ede-panel/applets/system-tray/Jamfile b/ede-panel/applets/system-tray/Jamfile new file mode 100644 index 0000000..2d34238 --- /dev/null +++ b/ede-panel/applets/system-tray/Jamfile @@ -0,0 +1,13 @@ +# +# $Id: Jamfile 2922 2009-11-05 15:18:51Z karijes $ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2011 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel applets system-tray ; + +PanelApplet system_tray : Tray.cpp TrayWindow.cpp ; diff --git a/ede-panel/applets/system-tray/Tray.cpp b/ede-panel/applets/system-tray/Tray.cpp new file mode 100644 index 0000000..227bae9 --- /dev/null +++ b/ede-panel/applets/system-tray/Tray.cpp @@ -0,0 +1,184 @@ +#include +#define FL_LIBRARY 1 +#include +#include +#include + +#include "Applet.h" +#include "Panel.h" +#include "Tray.h" +#include "TrayWindow.h" + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +EDELIB_NS_USING(netwm_window_get_icon) +EDELIB_NS_USING(netwm_window_get_title) + +/* multiple tray's are not allowed anyways so this can work */ +Tray *curr_tray = 0; + +static int handle_xevent(int e) { + if(fl_xevent->type == ClientMessage && fl_xevent->xclient.message_type == curr_tray->get_opcode()) { + if(fl_xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + E_DEBUG(E_STRLOC ": Dock request for %i\n", fl_xevent->xclient.data.l[2]); + curr_tray->embed_window(fl_xevent->xclient.data.l[2]); + return true; + } + + if(fl_xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE) { + E_DEBUG(E_STRLOC ": SYSTEM_TRAY_BEGIN_MESSAGE\n"); + return true; + } + + if(fl_xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE) { + E_DEBUG(E_STRLOC ": SYSTEM_TRAY_CANCEL_MESSAGE\n"); + return true; + } + } else if(fl_xevent->type == DestroyNotify) { + XDestroyWindowEvent xev = fl_xevent->xdestroywindow; + curr_tray->unembed_window(xev.window); + return false; + } + + return false; +} + +Tray::Tray() : Fl_Group(0, 0, 5, 25), opcode(0) { + box(FL_FLAT_BOX); + color(FL_RED); + register_notification_area(); +} + +Tray::~Tray() { + Atom sel; + char sel_name[20]; + + snprintf(sel_name, sizeof(sel_name), "_NET_SYSTEM_TRAY_S%d", fl_screen); + sel = XInternAtom(fl_display, sel_name, False); + XSetSelectionOwner(fl_display, sel, None, CurrentTime); +} + +void Tray::register_notification_area(void) { + Atom sel; + char sel_name[20]; + + snprintf(sel_name, sizeof(sel_name), "_NET_SYSTEM_TRAY_S%d", fl_screen); + sel = XInternAtom(fl_display, sel_name, False); + if(XGetSelectionOwner(fl_display, sel)) { + E_WARNING(E_STRLOC ": Notification area service is already provided by different program\n"); + return; + } + + /* register */ + XSetSelectionOwner(fl_display, sel, fl_message_window, CurrentTime); + if(XGetSelectionOwner(fl_display, sel) != fl_message_window) { + E_WARNING(E_STRLOC ": Unable to register notification area service\n"); + return; + } + + XClientMessageEvent xev; + xev.type = ClientMessage; + xev.message_type = XInternAtom(fl_display, "MANAGER", False); + xev.format = 32; + xev.data.l[0] = CurrentTime; + xev.data.l[1] = sel; + xev.data.l[2] = fl_message_window; + xev.data.l[3] = xev.data.l[4] = 0; + + XSendEvent(fl_display, RootWindow(fl_display, fl_screen), False, StructureNotifyMask, (XEvent*)&xev); + + opcode = XInternAtom(fl_display, "_NET_SYSTEM_TRAY_OPCODE", False); + message_data = XInternAtom(fl_display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); + + curr_tray = this; + Fl::add_handler(handle_xevent); +} + +static int get_parent(Window id, Window *parent) { + Window root, *children = 0; + unsigned int nchildren; + int s = XQueryTree(fl_display, id, &root, parent, &children, &nchildren); + if(s && children); + XFree(children); + return s; +} + +void Tray::embed_window(Window id) { + TrayWindow *win = new TrayWindow(25, 25); + win->end(); + win->set_image(netwm_window_get_icon(id)); + win->set_tooltip(netwm_window_get_title(id)); + + add_to_tray(win); + win->show(); + + XReparentWindow(fl_display, id, fl_xid(win), 0, 0); + /* remove any pixmap from reparented window, so 'TrayWindow' can display own content */ + XSetWindowBackgroundPixmap(fl_display, id, None); + + /* need to know when child dies */ + XSelectInput(fl_display, fl_xid(win), SubstructureNotifyMask); + + WinInfo i; + i.id = id; + i.win = win; + /* and to list */ + win_list.push_back(i); +} + +void Tray::unembed_window(Window id) { + WinListIt it = win_list.begin(), ite = win_list.end(); + + for(; it != ite; ++it) { + if((*it).id == id) { + remove_from_tray((*it).win); + win_list.erase(it); + return; + } + } +} + +void Tray::add_to_tray(Fl_Widget *win) { + win->position(x(), y()); + + insert(*win, 0); + int W = w() + win->w() + 5; + w(W); + + EDE_PANEL_GET_PANEL_OBJECT(this)->relayout(); +} + +void Tray::remove_from_tray(Fl_Widget *win) { + remove(win); + + int W = w() - win->w() - 5; + w(W); + + EDE_PANEL_GET_PANEL_OBJECT(this)->relayout(); +} + +int Tray::handle(int e) { + WinListIt it = win_list.begin(), ite = win_list.end(); + + for(; it != ite; ++it) { + Fl_Window *n = (*it).win; + if(Fl::event_x() >= n->x() && Fl::event_y() <= (n->x() + n->w()) && + Fl::event_y() >= n->y() && Fl::event_y() <= (n->y() + n->h())) + { + return n->handle(e); + } + } + + return Fl_Group::handle(e); +} + +EDE_PANEL_APPLET_EXPORT ( + Tray, + EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT, + "System Tray", + "0.1", + "empty", + "Sanel Zukan, Vedran Ljubovic" +) diff --git a/ede-panel/applets/system-tray/Tray.h b/ede-panel/applets/system-tray/Tray.h new file mode 100644 index 0000000..761967f --- /dev/null +++ b/ede-panel/applets/system-tray/Tray.h @@ -0,0 +1,36 @@ +#ifndef __TRAY_H__ +#define __TRAY_H__ + +#include +#include +#include + +EDELIB_NS_USING(list) + +struct WinInfo { + Window id; + Fl_Window *win; +}; + +typedef list WinList; +typedef list::iterator WinListIt; + +class Tray : public Fl_Group { +private: + Atom opcode, message_data; + WinList win_list; +public: + Tray(); + ~Tray(); + void register_notification_area(void); + Atom get_opcode(void) const { return opcode; } + void embed_window(Window id); + void unembed_window(Window id); + + void add_to_tray(Fl_Widget *w); + void remove_from_tray(Fl_Widget *w); + + int handle(int e); +}; + +#endif diff --git a/ede-panel/applets/system-tray/TrayWindow.cpp b/ede-panel/applets/system-tray/TrayWindow.cpp new file mode 100644 index 0000000..1b899f7 --- /dev/null +++ b/ede-panel/applets/system-tray/TrayWindow.cpp @@ -0,0 +1,58 @@ +#include +#include +#include "TrayWindow.h" + +/* scale image by keeping aspect ratio */ +static Fl_RGB_Image *aspect_scale(Fl_RGB_Image *orig, int W, int H) { + float aspect, neww, newh; + + neww = (float)orig->w(); + newh = (float)orig->h(); + + if(neww > W) { + aspect = (float)W / (float)neww; + neww *= aspect; + newh *= aspect; + } + + if(newh > H) { + aspect = (float)H / (float)newh; + neww *= aspect; + newh *= aspect; + } + + return (Fl_RGB_Image*)orig->copy((int)neww, (int)newh); +} + +TrayWindow::TrayWindow(int W, int H) : Fl_Window(W, H), img(0) { + box(FL_FLAT_BOX); +} + +void TrayWindow::set_image(Fl_RGB_Image *im) { + E_RETURN_IF_FAIL(im != 0); + + /* delete previous one */ + delete img; + + /* do not use full w/h, but leave some room for few pixels around image */ + img = aspect_scale(im, w() - 2, h() - 2); +} + +void TrayWindow::draw(void) { + Fl_Window::draw(); + if(img) { + int X = w()/2 - img->w()/2; + int Y = h()/2 - img->h()/2; + img->draw(X, Y); + } +} + +void TrayWindow::set_tooltip(char *t) { + if(!t) { + ttip.clear(); + tooltip(0); + } else { + ttip = t; + tooltip(ttip.c_str()); + } +} diff --git a/ede-panel/applets/system-tray/TrayWindow.h b/ede-panel/applets/system-tray/TrayWindow.h new file mode 100644 index 0000000..edb65fd --- /dev/null +++ b/ede-panel/applets/system-tray/TrayWindow.h @@ -0,0 +1,22 @@ +#ifndef __TRAYWINDOW_H__ +#define __TRAYWINDOW_H__ + +#include +#include +#include + +EDELIB_NS_USING(String) + +/* window dedicated to displaying tray window with icon resized to window size */ +class TrayWindow : public Fl_Window { +private: + Fl_RGB_Image *img; + String ttip; +public: + TrayWindow(int W, int H); + void set_image(Fl_RGB_Image *im); + void draw(void); + void set_tooltip(char *t); +}; + +#endif diff --git a/ede-panel/applets/taskbar/Taskbar.cpp b/ede-panel/applets/taskbar/Taskbar.cpp index 10e74de..df23050 100644 --- a/ede-panel/applets/taskbar/Taskbar.cpp +++ b/ede-panel/applets/taskbar/Taskbar.cpp @@ -70,7 +70,7 @@ static void net_event_cb(int action, Window xid, void *data) { Taskbar::Taskbar() : Fl_Group(0, 0, 40, 25), curr_active(NULL), prev_active(NULL), panel(NULL) { end(); - panel = EDE_PANEL_GET_PANEL_OBJECT; + panel = EDE_PANEL_GET_PANEL_OBJECT(this); /* assure display is openned */ fl_open_display();