From 27d1e03d7bdf8fcfe7292c06e40bc3e2fca9158e Mon Sep 17 00:00:00 2001 From: Denis Oliver Kropp Date: Tue, 19 Oct 2010 15:56:15 +0200 Subject: pluggit --- src/SourceWin32.cxx | 653 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 653 insertions(+) create mode 100644 src/SourceWin32.cxx (limited to 'src/SourceWin32.cxx') diff --git a/src/SourceWin32.cxx b/src/SourceWin32.cxx new file mode 100644 index 0000000..25ac45b --- /dev/null +++ b/src/SourceWin32.cxx @@ -0,0 +1,653 @@ +namespace PluggIt { + +D_DEBUG_DOMAIN( PluggIt_SourceWin32, "PluggIt/SourceWin32", "PluggIt Source Win32" ); + + +#include "ScreenHooks.h" + +const UINT specIpcCode = RegisterWindowMessage("HOOK.MESSAGE.CODE"); + + +class SourceWin32 extends Source, Runnable +{ + private HINSTANCE m_hInstance; + + private HWND m_root; + private HWND m_window; + + private DFBPoint m_position; + private DFBDimension m_size; + + private HBITMAP m_bitmap; + private void *m_pixels; + private int m_pitch; + + private HBITMAP m_bitmap2; + private void *m_pixels2; + private int m_pitch2; + + private HDC m_window_dc; + private HDC m_bitmap_dc; + private HDC m_bitmap_dc2; + + private Thread *m_thread; + + private Updates m_updates; + + private VIDEODRIVER m_driver; + private bool m_using_driver; + + private pthread_mutex_t m_lock; + private pthread_cond_t m_cond; + + + public class Config extends Source::Config { + friend class SourceWin32; + + public typedef enum { + WINDOW_SELECTION_NONE, + WINDOW_SELECTION_TITLE, + WINDOW_SELECTION_INTERACTIVE + } WindowSelection; + + private WindowSelection m_selection; + private std::string m_title; + private bool m_updating; + private bool m_use_driver; + + + public Config( WindowSelection selection, + const std::string &title = std::string(), + bool updating = false, bool use_driver = true ) + { + m_selection = selection; + m_title = title; + m_updating = updating; + m_use_driver = use_driver; + } + }; + + + public SourceWin32( View *view, const Config &config ) : Source( view, config ), m_updates(16) { + D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p )\n", __FUNCTION__, view ); + + m_hInstance = GetModuleHandle( NULL ); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> hInstance %p\n", m_hInstance ); + + + m_root = GetDesktopWindow(); + + 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 == 0) + throw new Exception( "Could not find window with title containing '%s'\n", config.m_title.c_str() ); + break; + + case Config::WINDOW_SELECTION_INTERACTIVE: + pickWindow(); + if (m_window == 0) + throw new Exception( "Could not interactively pick a window\n" ); + break; + + default: + throw new Exception( "Invalid window selection method\n" ); + } + + + RECT rect; + + GetClientRect( m_window, &rect ); + + m_position.x = rect.left; + m_position.y = rect.top; + + m_size.w = rect.right - rect.left; + m_size.h = rect.bottom - rect.top; + + queueUpdate( 0, 0, m_size.w - 1, m_size.h - 1 ); + + + std::string t = getWindowTitle( m_window ); + + D_INFO( "Source/Win32: Window %d,%d-%ux%u, name '%s'\n", + m_position.x, m_position.y, m_size.w, m_size.h, t.c_str() ); + + if (config.m_use_driver) + m_driver.VIDEODRIVER_start( m_position.x, m_position.y, m_size.w, m_size.h ); + + m_using_driver = m_driver.mypchangebuf != NULL; + + m_view->config( m_size ); + + + m_window_dc = GetDC( m_window ); + m_bitmap_dc = CreateCompatibleDC( m_window_dc ); + m_bitmap_dc2 = CreateCompatibleDC( m_window_dc ); + + + BITMAPINFO bitmap_info = {{ + biSize: sizeof(BITMAPINFOHEADER), + biWidth: m_size.w, + biHeight: m_size.h, + biPlanes: 1, + biBitCount: 32, + biCompression: BI_RGB, + biSizeImage: 0, + biXPelsPerMeter: 0, + biYPelsPerMeter: 0, + biClrUsed: 0, + biClrImportant: 0, + }}; + + m_bitmap = CreateDIBSection( m_window_dc, &bitmap_info, DIB_RGB_COLORS, &m_pixels, NULL, 0 ); + m_bitmap2 = CreateDIBSection( m_window_dc, &bitmap_info, DIB_RGB_COLORS, &m_pixels2, NULL, 0 ); + + + BITMAP bitmap; + + GetObject( m_bitmap, sizeof(BITMAP), &bitmap ); + + m_pitch = bitmap.bmWidthBytes; + + GetObject( m_bitmap2, sizeof(BITMAP), &bitmap ); + + m_pitch2 = bitmap.bmWidthBytes; + + + SelectObject( m_bitmap_dc, m_bitmap ); + SelectObject( m_bitmap_dc2, m_bitmap2 ); + + + pthread_mutex_init( &m_lock, NULL ); + pthread_cond_init( &m_cond, NULL ); + + + if (!m_using_driver) { + HWND w; + + WNDCLASSEX wndClass; + + ZeroMemory( &wndClass, sizeof(wndClass) ); + + wndClass.cbSize = sizeof(wndClass); + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = MsgWndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = GetModuleHandle(NULL); + wndClass.hIcon = NULL; + wndClass.hIconSm = NULL; + wndClass.hCursor = LoadCursor( NULL, IDC_UPARROW ); + wndClass.hbrBackground = (HBRUSH) GetStockObject( NULL_BRUSH ); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = "MsgWindow"; + + if (!RegisterClassEx( &wndClass )) + throw new Exception( "RegisterClassEx() failed!" ); + + + w = CreateWindowEx( 0, + "MsgWindow", + "MsgWindow", + WS_POPUP,// | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER, + CW_USEDEFAULT, + CW_USEDEFAULT, + 1, + 1, + NULL, + NULL, + GetModuleHandle(NULL), + this ); + + if (!setHook( w )) + throw new Exception( "Failed to set hooks!" ); + } + + + /* Create the thread object. */ + m_thread = new Thread( this, "SourceWin32" ); + + m_thread->start(); + } + + virtual ~SourceWin32() { + m_thread->interrupt(); + m_thread->join(); + delete m_thread; + + pthread_mutex_destroy( &m_lock ); + pthread_cond_destroy( &m_cond ); + + DeleteDC( m_window_dc ); + DeleteDC( m_bitmap_dc ); + DeleteDC( m_bitmap_dc2 ); + + DeleteObject( m_bitmap ); + DeleteObject( m_bitmap2 ); + } + + private void addRect( const RECT &rect ) { + if (rect.left >= rect.right) + return; + + if (rect.top >= rect.bottom) + return; + + DFBRegion region( rect.left, + rect.top, + rect.right - 1, + rect.bottom - 1 ); + + m_updates.addRegion( region ); + } + + private void run() { + if (m_using_driver) { + D_ASSERT( m_driver.mypchangebuf != NULL ); + + unsigned int counter = m_driver.mypchangebuf->counter; + unsigned int i; + + long long last_time = direct_clock_get_abs_millis(); + + D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ ); + + while (!m_thread->isInterrupted()) { + if (m_driver.mypchangebuf->counter < 1 || + m_driver.mypchangebuf->counter >= MAXCHANGES_BUF || + counter == m_driver.mypchangebuf->counter) + usleep( 10000 ); + + if (counter != m_driver.mypchangebuf->counter) { + D_DEBUG_AT( PluggIt_SourceWin32, " -> counter: %d -> %d\n", counter, m_driver.mypchangebuf->counter ); + + if (counter < m_driver.mypchangebuf->counter) { + for (i=counter+1; i<=m_driver.mypchangebuf->counter; i++) { + addRect( m_driver.mypchangebuf->pointrect[i].rect ); + } + } + else if (counter > m_driver.mypchangebuf->counter) { + for (i=counter+1; ipointrect[i].rect ); + } + for (i=1; i<=m_driver.mypchangebuf->counter; i++) { + addRect( m_driver.mypchangebuf->pointrect[i].rect ); + } + } + + counter = m_driver.mypchangebuf->counter; + } + + if (direct_clock_get_abs_millis() - last_time > 2000) { + DFBRegion region( 0, + 0, + m_size.w - 1, + m_size.h - 1 ); + + m_updates.addRegion( region ); + + last_time = direct_clock_get_abs_millis(); + } + + if (m_updates.num_regions() > 0) { + vector rects; + + m_updates.GetRectangles( rects ); + + + const DFBRegion &bounding = m_updates.bounding(); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n", + DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() ); + + m_updates.reset(); + + flushUpdates( rects ); + } + } + } + else { + D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ ); + + long long last_time = direct_clock_get_abs_millis(); + + while (!m_thread->isInterrupted()) { + if (((const Config&)m_config).m_updating) { + usleep( 100000 ); + + queueUpdate( 0, 0, m_size.w - 1, m_size.h - 1 ); + + pthread_mutex_lock( &m_lock ); + } + else { + pthread_mutex_lock( &m_lock ); + + if (m_updates.num_regions() == 0) + usleep( 10000 ); + //pthread_cond_wait( &m_cond, &m_lock ); + } + + if (direct_clock_get_abs_millis() - last_time > 2000) { + DFBRegion region( 0, + 0, + m_size.w - 1, + m_size.h - 1 ); + + m_updates.addRegion( region ); + + last_time = direct_clock_get_abs_millis(); + } + + + vector rects; + + if (m_updates.num_regions() > 0) { + m_updates.GetRectangles( rects ); + + + const DFBRegion &bounding = m_updates.bounding(); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> %4d,%4d-%4dx%4d (%d regions, %d rectangles)\n", + DFB_RECTANGLE_VALS_FROM_REGION( &bounding ), m_updates.num_regions(), rects.size() ); + + m_updates.reset(); + } + + pthread_mutex_unlock( &m_lock ); + + if (!rects.empty()) + flushUpdates( rects ); + } + } + } + +/**********************************************************************************************************************/ + + private void queueUpdate( int x1, int y1, int x2, int y2 ) { + D_DEBUG_AT( PluggIt_SourceWin32, "%s( %d,%d-%dx%d )\n", __FUNCTION__, x1, y1, x2 - x1 + 1, y2 - y1 + 1 ); + + pthread_mutex_lock( &m_lock ); + + DFBRegion damage( x1, y1, x2, y2 ); + + m_updates.addRegion( damage ); + + pthread_mutex_unlock( &m_lock ); + + pthread_cond_signal( &m_cond ); + } + + private bool flushUpdates( vector &rects ) { + D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ ); + + if (m_using_driver) { + m_view->update( rects, m_driver.myframebuffer, m_pitch ); + } + else { + for (unsigned int i=0; iupdate( rects, (void*)((char*) m_pixels + m_pitch * (m_size.h - 1)), - m_pitch ); + } + + D_DEBUG_AT( PluggIt_SourceWin32, " -> flush done\n" ); + + return true; + } + + + +/**********************************************************************************************************************/ + + private static std::string getWindowTitle( HWND window ) { + char buf[1024] = { 0 }; + + GetWindowText( window, buf, sizeof(buf) ); + + D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p ) -> '%s'\n", __FUNCTION__, window, buf ); + + return buf; + } + + +/**********************************************************************************************************************/ + + class EnumContext { + public const std::string &title; + public HWND window; + + public EnumContext( const std::string &title ) : title(title), window(0) {} + }; + + private static BOOL CALLBACK enumWindowsProc( HWND hwnd, + LPARAM lParam ) + { + EnumContext *ctx = reinterpret_cast( lParam ); + + D_DEBUG_AT( PluggIt_SourceWin32, "%s( %p )\n", __FUNCTION__, hwnd ); + + std::string title = getWindowTitle( hwnd ); + + if (title == ctx->title || title.find( ctx->title ) < title.size()) + ctx->window = hwnd; + + return ctx->window == NULL; + } + + private HWND findWindow( const std::string &title ) { + D_DEBUG_AT( PluggIt_SourceWin32, "%s( %s )\n", __FUNCTION__, title.c_str() ); + + EnumContext ctx( title ); + + EnumWindows( enumWindowsProc, reinterpret_cast( &ctx ) ); + + return ctx.window; + } + + +/**********************************************************************************************************************/ + + static LRESULT CALLBACK WndProc( HWND hwnd, // handle to window + UINT uMsg, // message identifier + WPARAM wParam, // first message parameter + LPARAM lParam ) // second message parameter + { + static SourceWin32 *thiz; + POINT point; + CREATESTRUCT *create; + + switch (uMsg) { + case WM_CREATE: + create = reinterpret_cast( lParam ); + thiz = reinterpret_cast( create->lpCreateParams ); + + SetCursor( LoadCursor( NULL, IDC_CROSS ) ); + break; + + case WM_LBUTTONDOWN: + ShowWindow( hwnd, SW_HIDE ); + + + GetCursorPos( &point ); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> Cursor position is %ld,%ld\n", point.x, point.y ); + + + thiz->m_window = WindowFromPoint( point ); + if (thiz->m_window) { + HWND parent; + + while ((parent = GetParent( thiz->m_window )) != NULL) + thiz->m_window = parent; + } + + D_DEBUG_AT( PluggIt_SourceWin32, " -> Window at cursor is %p: %s\n", + thiz->m_window, getWindowTitle( thiz->m_window ).c_str() ); + + DestroyWindow( hwnd ); + break; + + case WM_TIMER: + if (wParam == 666) + DestroyWindow( hwnd ); + break; + + case WM_DESTROY: + KillTimer( hwnd, 666 ); + PostQuitMessage( 0 ); + break; + + default: + SetTimer( hwnd, 666, 5000, NULL ); + + return DefWindowProc( hwnd, uMsg, wParam, lParam ); + } + + return 0; + } + + private void pickWindow() { + D_DEBUG_AT( PluggIt_SourceWin32, "%s()\n", __FUNCTION__ ); + + HDC hdc; + + hdc = GetDC( NULL ); + if (!hdc) + throw new Exception( "GetDC() returned NULL" ); + + RECT rect; + + GetClipBox( hdc, &rect ); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> Desktop size: %ldx%ld\n", + rect.right - rect.left, rect.bottom - rect.top ); + + ReleaseDC( NULL, hdc ); + + + + WNDCLASSEX wndClass; + + ZeroMemory( &wndClass, sizeof(wndClass) ); + + wndClass.cbSize = sizeof(wndClass); + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = m_hInstance; + wndClass.hIcon = NULL; + wndClass.hIconSm = NULL; + wndClass.hCursor = LoadCursor( NULL, IDC_UPARROW ); + wndClass.hbrBackground = (HBRUSH) GetStockObject( NULL_BRUSH ); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = "GrabWindow"; + + if (!RegisterClassEx( &wndClass )) + throw new Exception( "RegisterClassEx() failed!\n" ); + + + AdjustWindowRect( &rect, WS_CAPTION, FALSE ); + + + HWND grabwin; + + grabwin = CreateWindowEx( 0, + "GrabWindow", + "GrabWindow", + WS_POPUP,// | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER, + CW_USEDEFAULT, + CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + NULL, + NULL, + m_hInstance, + this ); + + SetWindowLong( grabwin, GWL_EXSTYLE, + GetWindowLong( grabwin, GWL_EXSTYLE ) | WS_EX_TRANSPARENT | WS_EX_TOPMOST ); + + ShowWindow( grabwin, SW_SHOW ); + + + + MSG msg; + + while (GetMessage( &msg, 0, 0, 0 )) { + TranslateMessage( &msg ); + + D_DEBUG_AT( PluggIt_SourceWin32, " -> Dispatching event (id %u)...\n", msg.message ); + + DispatchMessage( &msg ); + } + } + +/**********************************************************************************************************************/ + + static LRESULT CALLBACK MsgWndProc( HWND hwnd, // handle to window + UINT uMsg, // message identifier + WPARAM wParam, // first message parameter + LPARAM lParam ) // second message parameter + { + static SourceWin32 *thiz; + CREATESTRUCT *create; + + switch (uMsg) { + case WM_CREATE: + create = reinterpret_cast( lParam ); + thiz = reinterpret_cast( create->lpCreateParams ); + break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + + default: + if (uMsg == specIpcCode) { + int x1 = wParam >> 16; + int y1 = wParam & 0xffff; + int x2 = lParam >> 16; + int y2 = lParam & 0xffff; + + if (x1 < x2 && y1 < y2) + thiz->queueUpdate( x1, y1, x2 - 1, y2 - 1 ); + + break; + } + + return DefWindowProc( hwnd, uMsg, wParam, lParam ); + } + + return 0; + } + + public virtual int MainLoop() { + MSG msg; + + while (GetMessage( &msg, 0, 0, 0 )) { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + return 0; + } +}; + +} + -- cgit