|
|
|
/*
|
|
|
|
* Copyright (C) 2004 Girish Ramakrishnan All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this software; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
|
|
* USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// $Id: util.cpp,v 1.7 2005/01/29 09:56:08 cs19713 Exp $
|
|
|
|
|
|
|
|
#include "trace.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/Xmu/WinUtil.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <X11/cursorfont.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assert validity of the window id. Get window attributes for the heck of it
|
|
|
|
* and see if the request went through.
|
|
|
|
*/
|
|
|
|
bool isValidWindowId(Display *display, Window w)
|
|
|
|
{
|
|
|
|
XWindowAttributes attrib;
|
|
|
|
return XGetWindowAttributes(display, w, &attrib) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Checks if this window is a normal window (i.e)
|
|
|
|
* - Has a WM_STATE
|
|
|
|
* - Not modal window
|
|
|
|
* - Not a purely transient window (with no window type set)
|
|
|
|
* - Not a special window (desktop/menu/util) as indicated in the window type
|
|
|
|
*/
|
|
|
|
bool isNormalWindow(Display *display, Window w)
|
|
|
|
{
|
|
|
|
Atom __attribute__ ((unused)) type;
|
|
|
|
int __attribute__ ((unused)) format;
|
|
|
|
unsigned long __attribute__ ((unused)) left;
|
|
|
|
Atom *data = NULL;
|
|
|
|
unsigned long nitems;
|
|
|
|
Window transient_for = None;
|
|
|
|
|
|
|
|
static Atom wmState = XInternAtom(display, "WM_STATE", False);
|
|
|
|
static Atom windowState = XInternAtom(display, "_NET_WM_STATE", False);
|
|
|
|
static Atom modalWindow =
|
|
|
|
XInternAtom(display, "_NET_WM_STATE_MODAL", False);
|
|
|
|
|
|
|
|
static Atom windowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
|
|
|
|
static Atom normalWindow =
|
|
|
|
XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
|
|
|
|
static Atom dialogWindow =
|
|
|
|
XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
|
|
|
|
|
|
|
|
int ret = XGetWindowProperty(display, w, wmState, 0,
|
|
|
|
10, False, AnyPropertyType, &type, &format,
|
|
|
|
&nitems, &left, (unsigned char **) &data);
|
|
|
|
|
|
|
|
TRACE("0x%x (%i)", (unsigned) w, (unsigned) w);
|
|
|
|
|
|
|
|
if (ret != Success || data == NULL) return false;
|
|
|
|
TRACE("\tHas WM_STATE");
|
|
|
|
if (data) XFree(data);
|
|
|
|
|
|
|
|
ret = XGetWindowProperty(display, w, windowState, 0,
|
|
|
|
10, False, AnyPropertyType, &type, &format,
|
|
|
|
&nitems, &left, (unsigned char **) &data);
|
|
|
|
if (ret == Success)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < nitems; i++)
|
|
|
|
{
|
|
|
|
if (data[i] == modalWindow) break;
|
|
|
|
}
|
|
|
|
XFree(data);
|
|
|
|
if (i < nitems) { TRACE("\tMODAL"); return false; }
|
|
|
|
}
|
|
|
|
else TRACE("\tNo _NET_WM_STATE");
|
|
|
|
|
|
|
|
XGetTransientForHint(display, w, &transient_for);
|
|
|
|
TRACE("\tTransientFor=0x%x", (unsigned) transient_for);
|
|
|
|
|
|
|
|
ret = XGetWindowProperty(display, w, windowType, 0,
|
|
|
|
10, False, AnyPropertyType, &type, &format,
|
|
|
|
&nitems, &left, (unsigned char **) &data);
|
|
|
|
|
|
|
|
if ((ret == Success) && data)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < nitems; i++)
|
|
|
|
{
|
|
|
|
if (data[i] != normalWindow && data[i] != dialogWindow) break;
|
|
|
|
}
|
|
|
|
XFree(data);
|
|
|
|
TRACE("\tAbnormal = %i", (i == nitems));
|
|
|
|
return (i == nitems);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (transient_for == None);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the contents of the _NET_WM_PID (which is supposed to contain the
|
|
|
|
* process id of the application that created the window)
|
|
|
|
*/
|
|
|
|
pid_t pid(Display *display, Window w)
|
|
|
|
{
|
|
|
|
Atom actual_type;
|
|
|
|
int actual_format;
|
|
|
|
unsigned long nitems, leftover;
|
|
|
|
unsigned char *pid;
|
|
|
|
pid_t pid_return = -1;
|
|
|
|
|
|
|
|
if (XGetWindowProperty(display, w,
|
|
|
|
XInternAtom(display, "_NET_WM_PID", False), 0,
|
|
|
|
1, False, XA_CARDINAL, &actual_type,
|
|
|
|
&actual_format, &nitems, &leftover, &pid) == Success)
|
|
|
|
{
|
|
|
|
if (pid) pid_return = *(pid_t *) pid;
|
|
|
|
XFree(pid);
|
|
|
|
}
|
|
|
|
TRACE("0x%x has PID=%i", (unsigned) w, pid_return);
|
|
|
|
return pid_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sends ClientMessage to a window
|
|
|
|
*/
|
|
|
|
void sendMessage(Display* display, Window to, Window w, char* type,
|
|
|
|
int format, long mask, void* data, int size)
|
|
|
|
{
|
|
|
|
XEvent ev;
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
ev.xclient.type = ClientMessage;
|
|
|
|
ev.xclient.window = w;
|
|
|
|
ev.xclient.message_type = XInternAtom(display, type, True);
|
|
|
|
ev.xclient.format = format;
|
|
|
|
memcpy((char *) &ev.xclient.data, (const char *) data, size);
|
|
|
|
XSendEvent(display, to, False, mask, &ev);
|
|
|
|
XSync(display, False);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Grand Window Analyzer. Checks if window w has a expected pid of epid
|
|
|
|
* or a expected name of ename
|
|
|
|
*/
|
|
|
|
bool analyzeWindow(Display *display, Window w, pid_t epid, const TQString &ename)
|
|
|
|
{
|
|
|
|
XClassHint ch;
|
|
|
|
pid_t apid = pid(display, w);
|
|
|
|
|
|
|
|
TRACE("WID=0x%x EPID=%i APID=%i ExpectedName=%s", (unsigned) w, (unsigned) epid, (unsigned) apid,
|
|
|
|
ename.local8Bit());
|
|
|
|
if (epid == apid) return true;
|
|
|
|
// Only analyze window name if no process pid was provided, to avoid associating
|
|
|
|
// the wrong window to a given process
|
|
|
|
if (epid) return false;
|
|
|
|
|
|
|
|
// no plans to analyze windows without a name
|
|
|
|
char *window_name = NULL;
|
|
|
|
if (!XFetchName(display, w, &window_name)) return false;
|
|
|
|
TRACE("Window has name [%s]", window_name);
|
|
|
|
if (window_name) XFree(window_name); else return false;
|
|
|
|
|
|
|
|
bool this_is_our_man = false;
|
|
|
|
// lets try the program name
|
|
|
|
if (XGetClassHint(display, w, &ch))
|
|
|
|
{
|
|
|
|
if (TQString(ch.res_name).find(ename, 0, FALSE) != -1)
|
|
|
|
{
|
|
|
|
TRACE("ResName [%s] matched", ch.res_name);
|
|
|
|
this_is_our_man = true;
|
|
|
|
}
|
|
|
|
else if (TQString(ch.res_class).find(ename, 0, FALSE) != -1)
|
|
|
|
{
|
|
|
|
TRACE("ResClass [%s] matched", ch.res_class);
|
|
|
|
this_is_our_man = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// sheer desperation
|
|
|
|
char *wm_name = NULL;
|
|
|
|
XFetchName(display, w, &wm_name);
|
|
|
|
if (wm_name && (TQString(wm_name).find(ename, 0, FALSE) != -1))
|
|
|
|
{
|
|
|
|
TRACE("WM_NAME [%s] matched", wm_name);
|
|
|
|
this_is_our_man = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ch.res_class) XFree(ch.res_class);
|
|
|
|
if (ch.res_name) XFree(ch.res_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// its probably a good idea to check (obsolete) WM_COMMAND here
|
|
|
|
return this_is_our_man;
|
|
|
|
}
|
|
|
|
|
|
|
|
Window activeWindow(Display *display)
|
|
|
|
{
|
|
|
|
Atom active_window_atom = XInternAtom(display, "_NET_ACTIVE_WINDOW", True);
|
|
|
|
Atom type = None;
|
|
|
|
int format;
|
|
|
|
unsigned long nitems, after;
|
|
|
|
unsigned char *data = NULL;
|
|
|
|
int screen = DefaultScreen(display);
|
|
|
|
Window root = RootWindow(display, screen);
|
|
|
|
int r = XGetWindowProperty(display, root, active_window_atom,0, 1,
|
|
|
|
False, AnyPropertyType, &type, &format, &nitems, &after, &data);
|
|
|
|
|
|
|
|
Window w = None;
|
|
|
|
if ((r == Success) && data && (*(Window *)data != None))
|
|
|
|
w = *(Window *)data;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int revert;
|
|
|
|
XGetInputFocus(display, &w, &revert);
|
|
|
|
}
|
|
|
|
TRACE("Active window is 0x%x", (unsigned) w);
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Requests user to select a window by grabbing the mouse. A left click will
|
|
|
|
* select the application. Clicking any other button will abort the selection
|
|
|
|
*/
|
|
|
|
Window selectWindow(Display *display, const char **err)
|
|
|
|
{
|
|
|
|
int screen = DefaultScreen(display);
|
|
|
|
Window root = RootWindow(display, screen);
|
|
|
|
|
|
|
|
if (err) *err = NULL;
|
|
|
|
|
|
|
|
Cursor cursor = XCreateFontCursor(display, XC_draped_box);
|
|
|
|
if (cursor == None)
|
|
|
|
{
|
|
|
|
if (err) *err = "Failed to create XC_draped_box";
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (XGrabPointer(display, root, False, ButtonPressMask | ButtonReleaseMask,
|
|
|
|
GrabModeSync, GrabModeAsync, None, cursor, CurrentTime)
|
|
|
|
!= GrabSuccess)
|
|
|
|
{
|
|
|
|
if (err) *err = "Failed to grab mouse";
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
XAllowEvents(display, SyncPointer, CurrentTime);
|
|
|
|
XEvent event;
|
|
|
|
XWindowEvent(display, root, ButtonPressMask, &event);
|
|
|
|
Window selected_window =
|
|
|
|
(event.xbutton.subwindow == None) ? RootWindow(display, screen)
|
|
|
|
: event.xbutton.subwindow;
|
|
|
|
XUngrabPointer(display, CurrentTime);
|
|
|
|
XFreeCursor(display, cursor);
|
|
|
|
|
|
|
|
if (event.xbutton.button != Button1) return None;
|
|
|
|
return XmuClientWindow(display, selected_window);
|
|
|
|
}
|
|
|
|
|
|
|
|
void subscribe(Display *display, Window w, long mask, bool set)
|
|
|
|
{
|
|
|
|
Window root = RootWindow(display, DefaultScreen(display));
|
|
|
|
XWindowAttributes attr;
|
|
|
|
|
|
|
|
TRACE("Window=0x%x Mask=%ld Set=%i", (unsigned) w, mask, set);
|
|
|
|
|
|
|
|
XGetWindowAttributes(display, w == None ? root : w, &attr);
|
|
|
|
|
|
|
|
if (set && (attr.your_event_mask & mask == mask)) return;
|
|
|
|
if (!set && (attr.your_event_mask | mask == attr.your_event_mask)) return;
|
|
|
|
|
|
|
|
XSelectInput(display, w == None ? root : w,
|
|
|
|
set ? attr.your_event_mask | mask : attr.your_event_mask & mask);
|
|
|
|
XSync(display, False);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the state of the SystemTray and the Wid if it exists
|
|
|
|
SysTrayState sysTrayStatus(Display *display, Window *tray)
|
|
|
|
{
|
|
|
|
Screen *screen = XDefaultScreenOfDisplay(display);
|
|
|
|
Window sys_tray;
|
|
|
|
Atom selection = None;
|
|
|
|
|
|
|
|
char temp[50];
|
|
|
|
sprintf(temp, "_NET_SYSTEM_TRAY_S%i", XScreenNumberOfScreen(screen));
|
|
|
|
selection = XInternAtom(display, temp, True);
|
|
|
|
if (selection == None) return SysTrayAbsent;
|
|
|
|
sys_tray = XGetSelectionOwner(display, selection);
|
|
|
|
if (tray) *tray = sys_tray;
|
|
|
|
return sys_tray == None ? SysTrayHidden : SysTrayPresent;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool getCardinalProperty(Display *display, Window w, Atom prop, long *data)
|
|
|
|
{
|
|
|
|
Atom type;
|
|
|
|
int format;
|
|
|
|
unsigned long nitems, bytes;
|
|
|
|
unsigned char *d = NULL;
|
|
|
|
|
|
|
|
if (XGetWindowProperty(display, w, prop, 0, 1, False, XA_CARDINAL,&type,
|
|
|
|
&format, &nitems, &bytes, &d) == Success && d)
|
|
|
|
{
|
|
|
|
TRACE("%ld", *(long *)d);
|
|
|
|
if (data) *data = *(long *)d;
|
|
|
|
XFree(d);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TRACE("FAILED");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|