mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
327 lines
8.0 KiB
C++
327 lines
8.0 KiB
C++
|
#include "dock.h"
|
||
|
#include <efltk/Fl_Window.h>
|
||
|
#include <efltk/Fl_Button.h>
|
||
|
#include <efltk/Fl.h>
|
||
|
#include <efltk/x.h>
|
||
|
#include <X11/Xatom.h>
|
||
|
//#include <vector>
|
||
|
#include <efltk/Fl_WM.h>
|
||
|
|
||
|
#include <unistd.h>
|
||
|
#include <X11/Xproto.h> //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);
|
||
|
}
|