namespace PluggIt { D_DEBUG_DOMAIN( PluggIt_SourceWin32, "PluggIt/SourceWin32", "PluggIt Source Win32" ); #include "ScreenHooks.h" // Constants const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect"); const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect"); const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse"); const char szDesktopSink[] = "WinVNC desktop sink"; typedef BOOL (*SetHooksFn)(DWORD thread_id,UINT UpdateMsg,UINT CopyMsg,UINT MouseMsg,BOOL ddihook); 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; SetHooksFn SetHooks; 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 ); /* 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_cond_signal( &m_cond ); pthread_mutex_unlock( &m_lock ); } 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 DesktopWndProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam ) // second message parameter { PCOPYDATASTRUCT CDS; SourceWin32 *thiz = (SourceWin32*)GetWindowLong(hwnd, GWL_USERDATA); switch (uMsg) { case WM_DESTROY: PostQuitMessage( 0 ); break; case WM_COPYDATA: CDS = (PCOPYDATASTRUCT) lParam; if (CDS->dwData==112233) { DWORD mysize = CDS->cbData; char mytext[1024]; char *myptr; char split[4][6]; strcpy( mytext, (LPCSTR) CDS->lpData ); myptr = mytext; for (int j =0; j<(mysize/20);j++) { for (int i=0;i<4;i++) { strcpy(split[i]," "); strncpy(split[i],myptr,4); myptr = myptr+5; } thiz->queueUpdate( atoi(split[0]), atoi(split[1]), atoi(split[2]) - 1, atoi(split[3]) - 1 ); } } break; default: return DefWindowProc( hwnd, uMsg, wParam, lParam ); } return 0; } public virtual int MainLoop() { if (!m_using_driver) { HMODULE hModule; char szCurrentDir[MAX_PATH]; if (GetModuleFileName(NULL, szCurrentDir, MAX_PATH)) { char* p = strrchr(szCurrentDir, '\\'); if (p == NULL) return 0; *p = '\0'; strcat (szCurrentDir,"\\vnchooks.dll"); } hModule = LoadLibrary( szCurrentDir ); if (!hModule) throw new Exception( "Failed to load vnchooks.dll!" ); SetHooks = (SetHooksFn) GetProcAddress( hModule, "SetHooks" ); HWND w; WNDCLASSEX wndClass; ZeroMemory( &wndClass, sizeof(wndClass) ); wndClass.cbSize = sizeof(wndClass); wndClass.style = 0; wndClass.lpfnWndProc = DesktopWndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = GetModuleHandle(NULL); wndClass.hIcon = NULL; wndClass.hIconSm = NULL; wndClass.hCursor = NULL; wndClass.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = szDesktopSink; if (!RegisterClassEx( &wndClass )) throw new Exception( "RegisterClassEx() failed!" ); w = CreateWindowEx( 0, szDesktopSink, "WinVNC", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL, NULL, GetModuleHandle(NULL), NULL ); if (!w) throw new Exception( "Failed to create hook window!" ); // Set the "this" pointer for the window SetWindowLong( w, GWL_USERDATA, (long)this); if (!SetHooks( GetCurrentThreadId(), RFB_SCREEN_UPDATE, RFB_COPYRECT_UPDATE, RFB_MOUSE_UPDATE, false )) throw new Exception( "Failed to set hooks!" ); } MSG msg; printf( " -> RFB_SCREEN_UPDATE = %u\n", RFB_SCREEN_UPDATE ); printf( " -> RFB_COPYRECT_UPDATE = %u\n", RFB_COPYRECT_UPDATE ); while (WaitMessage()/*GetMessage( &msg, 0, 0, 0 )*/) { printf( " -> Dispatching event (id %u)...\n", msg.message ); if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { if (msg.message == RFB_SCREEN_UPDATE) { queueUpdate( (SHORT)LOWORD(msg.wParam), (SHORT)HIWORD(msg.wParam), (SHORT)LOWORD(msg.lParam) - 1, (SHORT)HIWORD(msg.lParam) - 1 ); } else { TranslateMessage( &msg ); DispatchMessage( &msg ); } } } printf( " -> EXIT!\n" ); return 0; } }; }