summaryrefslogtreecommitdiff
path: root/src/SourceX11.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/SourceX11.cxx')
-rw-r--r--src/SourceX11.cxx534
1 files changed, 534 insertions, 0 deletions
diff --git a/src/SourceX11.cxx b/src/SourceX11.cxx
new file mode 100644
index 0000000..339c8e7
--- /dev/null
+++ b/src/SourceX11.cxx
@@ -0,0 +1,534 @@
+extern "C" {
+#include <limits.h>
+}
+
+namespace PluggIt {
+
+D_DEBUG_DOMAIN( PluggIt_SourceX11, "PluggIt/SourceX11", "PluggIt Source X11" );
+
+
+class SourceX11 extends Source, Runnable
+{
+ private Display *m_display;
+ private Screen *m_screen;
+ private Window m_window;
+ private Window m_root;
+
+ private DFBPoint m_position;
+ private DFBDimension m_size;
+ private unsigned int m_depth;
+
+ private XImage *m_image;
+ private Pixmap m_pixmap;
+
+ private Bool m_shm;
+ private int m_shm_major;
+ private int m_shm_minor;
+ private XShmSegmentInfo m_shm_info;
+
+ private Damage m_xdamage;
+ private int m_xdamage_notify_event;
+ private XserverRegion m_xdamage_region;
+ private GC m_xdamage_copy_gc;
+
+ private Thread *m_thread;
+
+ private Updates m_updates;
+
+
+
+ public class Config extends Source::Config {
+ friend class SourceX11;
+
+ public typedef enum {
+ WINDOW_SELECTION_NONE,
+ WINDOW_SELECTION_TITLE,
+ WINDOW_SELECTION_INTERACTIVE
+ } WindowSelection;
+
+ private WindowSelection m_selection;
+ private std::string m_title;
+
+
+ public Config( WindowSelection selection, const std::string &title = std::string() ) {
+ m_selection = selection;
+ m_title = title;
+ }
+ };
+
+
+ public SourceX11( View *view, const Config &config ) : m_updates(16), Source( view, config ) {
+ D_DEBUG_AT( PluggIt_SourceX11, "%s( %p )\n", __FUNCTION__, view );
+
+ XInitThreads();
+
+ m_display = XOpenDisplay( getenv("DISPLAY") );
+ if (!m_display)
+ throw new Exception( "Error in XOpenDisplay for '%s'!\n", getenv("DISPLAY") );
+
+ XLockDisplay( m_display );
+
+ m_screen = DefaultScreenOfDisplay( m_display );
+ m_root = RootWindowOfScreen( m_screen );
+
+ switch (config.m_selection) {
+ case Config::WINDOW_SELECTION_NONE:
+ m_window = m_root;
+ break;
+
+ case Config::WINDOW_SELECTION_TITLE:
+ m_window = findWindow( config.m_title );
+ if (m_window == None)
+ throw new Exception( "Could not find window with title containing '%s'\n", config.m_title.c_str() );
+ break;
+
+ case Config::WINDOW_SELECTION_INTERACTIVE:
+ m_window = pickWindow();
+ if (m_window == None)
+ throw new Exception( "Could not interactively pick a window\n" );
+ break;
+
+ default:
+ throw new Exception( "Invalid window selection method\n" );
+ }
+
+ unsigned int tmp_u;
+ int tmp_i;
+
+ XGetGeometry( m_display, m_window, &m_root, &m_position.x, &m_position.y,
+ (unsigned int*) &m_size.w, (unsigned int*) &m_size.h, &tmp_u, &m_depth );
+
+
+ std::string t = getWindowTitle( m_window );
+
+ D_INFO( "Source/X11: Window %d,%d-%ux%u, depth %u, name '%s'\n",
+ m_position.x, m_position.y, m_size.w, m_size.h, m_depth, t.c_str() );
+
+
+ if (XShmQueryExtension( m_display ))
+ XShmQueryVersion( m_display, &m_shm_major, &m_shm_minor, &m_shm );
+
+ initXDamage();
+
+ if (m_shm) {
+ if (!initXShmImage()) {
+ m_shm = false;
+
+ initXImage();
+ }
+ }
+ else
+ initXImage();
+
+ XUnlockDisplay( m_display );
+
+
+ m_view->config( m_size );
+
+
+ /* Create the thread object. */
+ m_thread = new Thread( this, "SourceX11" );
+
+ m_thread->start();
+ }
+
+ virtual ~SourceX11() {
+ XDestroyImage( m_image );
+
+ if (m_shm) {
+ XShmDetach( m_display, &m_shm_info );
+
+ shmdt( m_shm_info.shmaddr );
+ }
+ }
+
+
+/**********************************************************************************************************************/
+
+ private void run() {
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ XLockDisplay( m_display );
+
+ while (true) {
+ XEvent event;
+
+ XNextEvent( m_display, &event );
+
+ if (event.type == m_xdamage_notify_event)
+ handleXDamage( reinterpret_cast<XDamageNotifyEvent*>( &event ) );
+
+ if (!XPending( m_display ))
+ flushXDamage();
+ }
+
+ XUnlockDisplay( m_display );
+ }
+
+
+/**********************************************************************************************************************/
+
+ private void initXDamage() {
+ int event_base, error_base;
+ int major, minor;
+ XGCValues values;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ if (!XDamageQueryExtension( m_display, &event_base, &error_base ))
+ throw new Exception( "XDamageQueryExtension" );
+
+ if (!XDamageQueryVersion( m_display, &major, &minor ) || major != 1)
+ throw new Exception( "XDamageQueryVersion" );
+
+ m_xdamage_notify_event = event_base + XDamageNotify;
+
+ m_xdamage = XDamageCreate( m_display, m_window, XDamageReportDeltaRectangles );
+ if (m_xdamage == None)
+ throw new Exception( "XDamageCreate" );
+
+ m_xdamage_region = XFixesCreateRegion( m_display, NULL, 0 );
+ if (m_xdamage_region == None) {
+ XDamageDestroy( m_display, m_xdamage );
+ m_xdamage = None;
+ throw new Exception( "XFixesCreateRegion" );
+ }
+
+ values.subwindow_mode = IncludeInferiors;
+
+ m_xdamage_copy_gc = XCreateGC( m_display, m_window, GCSubwindowMode, &values );
+ }
+
+ private void handleXDamage( const XDamageNotifyEvent *event ) {
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> %4d,%4d-%4dx%4d\n",
+ event->area.x, event->area.y, event->area.width, event->area.height );
+
+
+ DFBRegion damage( event->area.x, event->area.y,
+ event->area.x + event->area.width - 1,
+ event->area.y + event->area.height - 1 );
+
+ m_updates.addRegion( damage );
+ }
+
+ private void flushXDamage() {
+ vector<DFBRectangle_C> rects;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ m_updates.GetRectangles( rects );
+
+ if (rects.empty())
+ return;
+
+ const DFBRegion &bounding = m_updates.bounding();
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n",
+ DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() );
+
+ m_updates.reset();
+
+
+ XRectangle xdamage[rects.size()];
+
+ for (int i=0; i<rects.size(); i++) {
+ D_DEBUG_AT( PluggIt_SourceX11, " -> [%2d] %4d,%4d-%4dx%4d\n", i, DFB_RECTANGLE_VALS( &rects[i] ) );
+
+ xdamage[i].x = rects[i].x;
+ xdamage[i].y = rects[i].y;
+ xdamage[i].width = rects[i].w;
+ xdamage[i].height = rects[i].h;
+ }
+
+ XFixesSetRegion( m_display, m_xdamage_region, xdamage, rects.size() );
+ XDamageSubtract( m_display, m_xdamage, m_xdamage_region, None );
+
+
+ if (m_shm) {
+ for (int i=0; i<rects.size(); i++) {
+ XCopyArea( m_display,
+ m_window,
+ m_pixmap,
+ m_xdamage_copy_gc,
+ rects[i].x,
+ rects[i].y,
+ rects[i].w,
+ rects[i].h,
+ rects[i].x,
+ rects[i].y );
+ }
+
+ XSync( m_display, False );
+ }
+ else {
+ for (int i=0; i<rects.size(); i++) {
+ XGetSubImage( m_display,
+ m_window,
+ rects[i].x,
+ rects[i].y,
+ rects[i].w,
+ rects[i].h,
+ AllPlanes,
+ ZPixmap,
+ m_image,
+ rects[i].x,
+ rects[i].y );
+ }
+
+ XSync( m_display, False );
+ }
+
+ for (int i=0; i<rects.size(); i++) {
+ D_DEBUG_AT( PluggIt_SourceX11, " -> [%2d] %4d,%4d-%4dx%4d\n", i, DFB_RECTANGLE_VALS( &rects[i] ) );
+ }
+
+ m_view->update( rects, m_image->data, m_image->bytes_per_line );
+ }
+
+/**********************************************************************************************************************/
+
+ private bool initXShmImage() {
+ m_image = XShmCreateImage( m_display,
+ DefaultVisual( m_display, 0 ),
+ m_depth,
+ ZPixmap,
+ NULL,
+ &m_shm_info,
+ m_size.w,
+ m_size.h );
+ if (!m_image)
+ return false;
+
+ m_shm_info.shmid = shmget( IPC_PRIVATE, m_image->bytes_per_line * m_image->height, IPC_CREAT | 0600 );
+ if (m_shm_info.shmid == -1) {
+ XDestroyImage( m_image );
+ return false;
+ }
+
+ m_shm_info.readOnly = False;
+ m_shm_info.shmaddr = (char*) shmat( m_shm_info.shmid, 0, 0 );
+
+ if (m_shm_info.shmaddr == (char*) -1) {
+ shmctl( m_shm_info.shmid, IPC_RMID, 0 );
+ XDestroyImage( m_image );
+ return false;
+ }
+
+ m_image->data = m_shm_info.shmaddr;
+
+
+ XShmAttach( m_display, &m_shm_info );
+ XSync( m_display, False );
+
+ shmctl( m_shm_info.shmid, IPC_RMID, 0 );
+
+
+ m_pixmap = XShmCreatePixmap( m_display, m_window, m_image->data, &m_shm_info,
+ m_image->width, m_image->height, m_image->depth );
+ if (!m_pixmap) {
+ shmdt( m_shm_info.shmaddr );
+ XShmDetach( m_display, &m_shm_info );
+ XDestroyImage( m_image );
+ return false;
+ }
+
+ return true;
+ }
+
+ private void initXImage() {
+ m_image = XCreateImage( m_display,
+ DefaultVisual( m_display, 0 ),
+ m_depth,
+ ZPixmap,
+ 0,
+ NULL,
+ m_size.w,
+ m_size.h,
+ 32,
+ 0 );
+ if (!m_image)
+ throw new Exception( "XCreateImage" );
+
+ m_image->data = (char*) malloc( m_image->bytes_per_line * m_image->height );
+ if (!m_image->data) {
+ XDestroyImage( m_image );
+ throw new OutOfMemoryException();
+ }
+ }
+
+
+/**********************************************************************************************************************/
+
+ private std::string getWindowTitle( Window window ) {
+ std::string str;
+ char *name = NULL;
+ Atom actual_type;
+ int format;
+ unsigned long n;
+ unsigned long extra;
+
+ XGetWindowProperty( m_display, window, XA_WM_NAME, 0L, 100L,
+ False, AnyPropertyType, &actual_type, &format, &n,
+ &extra, (unsigned char **) &name );
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s( %u ) -> '%s'\n", __FUNCTION__, window, name );
+
+ if (name) {
+ str.append( name );
+
+ XFree( name );
+ }
+
+ return str;
+ }
+
+ private Window checkWindow( Window window,
+ const std::string &title )
+ {
+ std::string t;
+ Window w = None, root, parent, *children;
+ unsigned int num_children;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s( %u, %s )\n", __FUNCTION__, window, title.c_str() );
+
+ t = getWindowTitle( window );
+ if (t == title || t.find( title ) < t.size())
+ return window;
+
+ XQueryTree( m_display, window, &root, &parent, &children, &num_children );
+
+ for (unsigned int i=0; i<num_children; i++) {
+ w = checkWindow( children[i], title );
+ if (w)
+ break;
+ }
+
+ XFree( children );
+
+ return w;
+ }
+
+ private Window findWindow( const std::string &title ) {
+ Window win;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s( %s )\n", __FUNCTION__, title.c_str() );
+
+ win = checkWindow( m_root, title );
+ if (win)
+ return lookupManagedWindow( win );
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> not found\n" );
+
+ return None;
+ }
+
+
+/**********************************************************************************************************************/
+
+ private Window pickWindow() {
+ int loops;
+ Window win = None;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> Grabbing pointer...\n" );
+
+ XGrabPointer( m_display, m_root, False, ButtonPressMask | PointerMotionMask,
+ GrabModeAsync, GrabModeAsync, None, XCreateFontCursor( m_display, XC_cross ), CurrentTime );
+
+ for (loops=0; loops < 100 && win == None; loops++) {
+ XEvent event;
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> Waiting for event...\n" );
+
+ if (!XPending( m_display )) {
+ usleep( 40000 );
+ continue;
+ }
+
+ XNextEvent( m_display, &event );
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> event type %d\n", event.type );
+
+ switch (event.type) {
+ case ButtonPress:
+ D_DEBUG_AT( PluggIt_SourceX11, " -> button %d press at %d,%d\n",
+ event.xbutton.button, event.xbutton.x, event.xbutton.y );
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> sub window %u: '%s'\n",
+ event.xbutton.subwindow, getWindowTitle( event.xbutton.subwindow ).c_str() );
+
+ win = lookupManagedWindow( event.xbutton.subwindow );
+ break;
+
+ default:
+ break;
+ }
+
+ loops = 0;
+ }
+
+ XUngrabPointer( m_display, CurrentTime );
+
+ return win;
+ }
+
+
+/**********************************************************************************************************************/
+
+ private Window lookupManagedWindow( Window window ) {
+ int i, result;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned long *prop;
+ Atom ret_type = None;
+ int ret_format;
+ Atom wm_state_atom;
+
+ D_DEBUG_AT( PluggIt_SourceX11, "%s()\n", __FUNCTION__ );
+
+ wm_state_atom = XInternAtom( m_display, "WM_STATE", False );
+
+
+ D_DEBUG_AT( PluggIt_SourceX11, " -> Retrieving WM_STATE (Atom %d)...\n", wm_state_atom );
+
+ result = XGetWindowProperty( m_display, window, wm_state_atom,
+ 0, LONG_MAX, False, wm_state_atom,
+ &ret_type, &ret_format, &nitems,
+ &bytes_after, (unsigned char**) &prop );
+ if (result == Success) {
+ XFree( prop );
+
+ if (ret_type == wm_state_atom) {
+ D_DEBUG_AT( PluggIt_SourceX11, " -> OK\n" );
+ return window;
+ }
+ }
+
+
+
+ Window root;
+ Window parent;
+ Window *children = NULL;
+ Window win = None;
+ unsigned int num_children;
+
+ result = XQueryTree( m_display, window, &root, &parent, &children, &num_children );
+ if (!result)
+ return None;
+
+ for (i=0; i<num_children && win == None; i++)
+ win = lookupManagedWindow( children[i] );
+
+ if (children)
+ XFree (children);
+
+ return win;
+ }
+
+};
+
+}
+