extern "C" { #include } 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 ) : Source( view, config ), m_updates(16) { 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; 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( &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 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, %zd 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 [%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 [%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( %lu ) -> '%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( %lu, %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 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 %lu: '%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 %ld)...\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