#include "dock.h" #include #include #include #include #include //#include #include #include #include //For CARD32 #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 Dock *currentDock; //Can't pass a pointer to handle_x11_event; must be a better way though int panelheight=0; struct straydata { Window id; Fl_Window *win; straydata *next; } *traydata; Dock::Dock() : Fl_Group(0,0,0,0) { register_notification_area(); // box(FL_THIN_DOWN_BOX); color(FL_INVALID_COLOR); //Draw with parent color layout_align(FL_ALIGN_RIGHT); layout_spacing(1); traydata=0; end(); winstate=0; } Dock::~Dock() { Atom selection_atom; char selection_atom_name[20]; sprintf(selection_atom_name, "_NET_SYSTEM_TRAY_S%d", fl_screen); selection_atom = XInternAtom (fl_display, selection_atom_name, false); XSetSelectionOwner(fl_display, selection_atom, None, CurrentTime); } void Dock::register_notification_area() { Atom selection_atom; char selection_atom_name[20]; sprintf(selection_atom_name, "_NET_SYSTEM_TRAY_S%d", fl_screen); selection_atom = XInternAtom (fl_display, selection_atom_name, false); if (XGetSelectionOwner(fl_display, selection_atom)) { printf("The notification area service is being provided by a different program. I'll just handle EDE components. \n"); return; } //Register XSetSelectionOwner(fl_display, selection_atom, fl_message_window, CurrentTime); //Check registration was successful if (XGetSelectionOwner(fl_display, selection_atom) != fl_message_window) { printf("Could not register as 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] = selection_atom; xev.data.l[2] = fl_message_window; xev.data.l[3] = 0; 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); currentDock = this; Fl::add_handler(handle_x11_event); } int Dock::handle_x11_event(int e) { if (fl_xevent.type == ClientMessage && fl_xevent.xclient.message_type == currentDock->opcode ) { if (fl_xevent.xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { currentDock->embed_window(fl_xevent.xclient.data.l[2]); return true; } if (fl_xevent.xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE) { return true; } if (fl_xevent.xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE) { return true; } } else if (fl_xevent.type == DestroyNotify) { XDestroyWindowEvent xev = fl_xevent.xdestroywindow; // Loop trough linked list to find destroyed window struct straydata *p=traydata; struct straydata *p1=0; while (p!=0) { if (p->id == xev.window) { currentDock->remove_from_tray(p->win); delete p->win; if (p1 == 0) traydata = p->next; else p1->next = p->next; delete p; break; } p1=p; p=p->next; } } return false; } // Helper for Dock::embed_window() // Kopied from kdelibs/kdeui/qxembed.cpp static int get_parent(Window winid, Window *out_parent) { Window root, *children=0; unsigned int nchildren; int st = XQueryTree(fl_display, winid, &root, out_parent, &children, &nchildren); if (st && children) XFree(children); return st; } void Dock::embed_window(Window id) { if (id==0) return; // Store window id in a linked list structure struct straydata *p=traydata; struct straydata *p1; while (p!=0) { // Sometimes the app will reuse the same id on next start? // if (p->id == id) // return; p1=p; p=p->next; } p = new straydata; if (traydata == 0) { traydata=p; } else { p1->next = p; } p->id = id; p->next = 0; Fl_Window *win = new Fl_Window(24, 24); win->end(); this->add_to_tray(win); win->show(); p->win = win; // printf("id: %ld -> %ld\n", id, fl_xid(win)); XReparentWindow(fl_display, id, fl_xid(win), 0, 0); // Hack to get KDE icons working... Window parent = 0; get_parent(id, &parent); // printf(" ++ parent: %ld\n", parent); XMapWindow(fl_display, id); //Need to know when child dies XSelectInput(fl_display, fl_xid(win), SubstructureNotifyMask); /* Some ideas from KDE code... consider using (sometime) ---------------- Does this have any effect? static Atom hack_atom = XInternAtom( fl_display, "_KDE_SYSTEM_TRAY_EMBEDDING", False ); XChangeProperty( fl_display, id, hack_atom, hack_atom, 32, PropModeReplace, NULL, 0 ); ..... XDeleteProperty( fl_display, id, hack_atom ); ---------------- Force window to withdraw (?) // Helper for Dock::embed_window() // Kopied from kdelibs/kdeui/qxembed.cpp static bool wstate_withdrawn( Window winid ) { static Atom _XA_WM_STATE = 0; if(!_XA_WM_STATE) _XA_WM_STATE = XInternAtom(fl_display, "WM_STATE", False); Atom type; int format; unsigned long length, after; unsigned char *data; int r = XGetWindowProperty( fl_display, winid, _XA_WM_STATE, 0, 2, false, AnyPropertyType, &type, &format, &length, &after, &data ); bool withdrawn = true; // L1610: Non managed windows have no WM_STATE property. // Returning true ensures that the loop L1711 stops. if ( r == Success && data && format == 32 ) { uint32 *wstate = (uint32*)data; withdrawn = (*wstate == WithdrawnState ); XFree( (char *)data ); } return withdrawn; } if ( !wstate_withdrawn(id) ) { XWithdrawWindow(fl_display, id, fl_screen); XFlush(fl_display); // L1711: See L1610 while (!wstate_withdrawn(id)) sleep(1); } ---------------- XEMBED support: #define XEMBED_EMBEDDED_NOTIFY 0 #define XEMBED_WINDOW_ACTIVATE 1 #define XEMBED_WINDOW_DEACTIVATE 2 #define XEMBED_REQUEST_FOCUS 3 #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 #define XEMBED_FOCUS_NEXT 6 #define XEMBED_FOCUS_PREV 7 #define XEMBED_MODALITY_ON 10 // L0500: Helper to send XEmbed messages. static void sendXEmbedMessage( Window window, long message, long detail = 0, long data1 = 0, long data2 = 0) { static Atom xembed=0; if (!xembed) xembed = XInternAtom( fl_display, "_XEMBED", False ); if (!window) return; XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = xembed; ev.xclient.format = 32; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = message; ev.xclient.data.l[2] = detail; ev.xclient.data.l[3] = data1; ev.xclient.data.l[4] = data2; XSendEvent(fl_display, window, false, NoEventMask, &ev); XSync(fl_display, false); } static Atom _XA_XEMBED_INFO = 0; if(!_XA_XEMBED_INFO) _XA_XEMBED_INFO = XInternAtom(fl_display, "_XEMBED_INFO", False); int status=1; CARD32* prop = (CARD32*)getProperty(id, _XA_XEMBED_INFO, XA_CARDINAL, 0, &status); if (status == 0) { // Not XEMBED ....... } else { sendXEmbedMessage( id, XEMBED_EMBEDDED_NOTIFY, 0, fl_xid(win) ); } */ } void Dock::add_to_tray(Fl_Widget *w) { insert(*w, 0); w->layout_align(FL_ALIGN_LEFT); w->show(); int new_width = this->w() + w->width() + layout_spacing(); this->w(new_width); parent()->relayout(); Fl::redraw(); } void Dock::remove_from_tray(Fl_Widget *w) { remove(w); int new_width = this->w() - w->width() - layout_spacing(); this->w(new_width); parent()->relayout(); Fl::redraw(); } int Dock::handle(int event) { struct straydata *p=traydata; while (p!=0) { Fl_Window *w = p->win; if (Fl::event_x()>=w->x() && Fl::event_x()<=(w->x() + w->w()) && Fl::event_y()>=w->y() && Fl::event_y()<=(w->y() + w->h())) { int ret = w->handle(event); w->throw_focus(); return ret; } p=p->next; } return Fl_Group::handle(event); }