diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-09-19 21:25:46 +0300 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-10-14 15:06:41 +0200 |
commit | c1b79eb035fa158fb2ac3bc8e559809611070016 (patch) | |
tree | 3348dd749a700dedf87c9b16fe8be77c62928df8 /client/windows/platform.cpp | |
download | spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.gz spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.xz spice-c1b79eb035fa158fb2ac3bc8e559809611070016.zip |
fresh start
Diffstat (limited to 'client/windows/platform.cpp')
-rw-r--r-- | client/windows/platform.cpp | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp new file mode 100644 index 00000000..773fa614 --- /dev/null +++ b/client/windows/platform.cpp @@ -0,0 +1,786 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program 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 program 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 program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" + +#include "platform.h" +#include "win_platform.h" +#include "utils.h" +#include "threads.h" +#include "debug.h" +#include "monitor.h" +#include "record.h" +#include "playback.h" +#include "cursor.h" +#include "named_pipe.h" + +#define WM_USER_WAKEUP WM_USER +#define NUM_TIMERS 100 + +int gdi_handlers = 0; +extern HINSTANCE instance; + +class DefaultEventListener: public Platform::EventListener { +public: + virtual void on_app_activated() {} + virtual void on_app_deactivated() {} + virtual void on_monitors_change() {} +}; + +static DefaultEventListener default_event_listener; +static Platform::EventListener* event_listener = &default_event_listener; +static HWND paltform_win; +static HANDLE main_tread; + +struct Timer { + TimerID id; + timer_proc_t proc; + void* opaque; + Timer *next; +}; + +Timer timers[NUM_TIMERS]; +Timer* free_timers = NULL; +Mutex timers_lock; + +static void free_timer(Timer* timer) +{ + Lock lock(timers_lock); + timer->proc = NULL; + timer->next = free_timers; + free_timers = timer; +} + +static void init_timers() +{ + for (int i = 0; i < NUM_TIMERS; i++) { + timers[i].id = i; + free_timer(&timers[i]); + } +} + +static Timer* alloc_timer() +{ + Timer* timer; + + Lock lock(timers_lock); + if (!(timer = free_timers)) { + return NULL; + } + + free_timers = free_timers->next; + return timer; +} + +static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_TIMER: { + TimerID id = wParam - 1; + ASSERT(id < NUM_TIMERS); + Timer* timer = &timers[id]; + timer->proc(timer->opaque, id); + break; + } + case WM_USER_WAKEUP: { + break; + } + case WM_ACTIVATEAPP: + if (wParam) { + event_listener->on_app_activated(); + } else { + event_listener->on_app_deactivated(); + } + break; + case WM_DISPLAYCHANGE: + event_listener->on_monitors_change(); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +static void create_message_wind() +{ + WNDCLASSEX wclass; + ATOM class_atom; + HWND window; + + const LPCWSTR class_name = L"spicec_platform_wclass"; + + wclass.cbSize = sizeof(WNDCLASSEX); + wclass.style = 0; + wclass.lpfnWndProc = PlatformWinProc; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + wclass.hInstance = instance; + wclass.hIcon = NULL; + wclass.hCursor = NULL; + wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wclass.lpszMenuName = NULL; + wclass.lpszClassName = class_name; + wclass.hIconSm = NULL; + + if ((class_atom = RegisterClassEx(&wclass)) == 0) { + THROW("register class failed"); + } + + if (!(window = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) { + THROW("create message window failed"); + } + + paltform_win = window; +} + +void Platform::send_quit_request() +{ + ASSERT(GetCurrentThread() == main_tread); + PostQuitMessage(0); +} + +static std::vector<HANDLE> events; +static std::vector<EventOwner*> events_owners; + +void WinPlatform::add_event(EventOwner& event_owner) +{ + ASSERT(main_tread == GetCurrentThread()); + int size = events.size(); + if (size == MAXIMUM_WAIT_OBJECTS - 1) { + THROW("reached maximum allowed events to wait for"); + } + events.resize(size + 1); + events_owners.resize(size + 1); + events[size] = event_owner.get_event_handle(); + events_owners[size] = &event_owner; +} + +void WinPlatform::remove_event(EventOwner& event_owner) +{ + ASSERT(main_tread == GetCurrentThread()); + int size = events.size(); + for (int i = 0; i < size; i++) { + if (events_owners[i] == &event_owner) { + for (i++; i < size; i++) { + events[i - 1] = events[i]; + events_owners[i - 1] = events_owners[i]; + } + events.resize(size - 1); + events_owners.resize(size - 1); + return; + } + } + THROW("event owner not found"); +} + +void Platform::wait_events() +{ + if (!events.size()) { + if (!WaitMessage()) { + THROW("wait failed %d", GetLastError()); + } + return; + } + + DWORD r = MsgWaitForMultipleObjectsEx(events.size(), &events[0], INFINITE, QS_ALLINPUT, 0); + if (r == WAIT_OBJECT_0 + events.size()) { + return; + } + if (r >= WAIT_OBJECT_0 && r <= WAIT_OBJECT_0 + events.size() - 1) { + events_owners[r - WAIT_OBJECT_0]->on_event(); + } else if (r == WAIT_FAILED) { + THROW("wait multiple failed %d", GetLastError()); + } else { + THROW("unexpected wait return %u", r); + } +} + +NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface) +{ + return (ListenerRef)(new WinListener(name, listener_interface)); +} + +void NamedPipe::destroy(ListenerRef listener_ref) +{ + delete (WinListener *)listener_ref; +} + +void NamedPipe::destroy_connection(ConnectionRef conn_ref) +{ + delete (WinConnection *)conn_ref; +} + +int32_t NamedPipe::read(ConnectionRef conn_ref, uint8_t *buf, int32_t size) +{ + return ((WinConnection *)conn_ref)->read(buf, size); +} + +int32_t NamedPipe::write(ConnectionRef conn_ref, const uint8_t *buf, int32_t size) +{ + return ((WinConnection *)conn_ref)->write(buf, size); +} + +void Platform::wakeup() +{ + if (!PostMessage(paltform_win, WM_USER_WAKEUP, 0, 0)) { + THROW("post failed %d", GetLastError()); + } +} + +bool Platform::process_events() +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return true; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return false; +} + +void Platform::msleep(unsigned int msec) +{ + Sleep(msec); +} + +void Platform::yield() +{ + Sleep(0); +} + +void Platform::set_thread_priority(void* thread, Platform::ThreadPriority in_priority) +{ + ASSERT(thread == NULL); + int priority; + + switch (in_priority) { + case PRIORITY_TIME_CRITICAL: + priority = THREAD_PRIORITY_TIME_CRITICAL; + break; + case PRIORITY_HIGH: + priority = THREAD_PRIORITY_HIGHEST; + break; + case PRIORITY_ABOVE_NORMAL: + priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case PRIORITY_NORMAL: + priority = THREAD_PRIORITY_NORMAL; + break; + case PRIORITY_BELOW_NORMAL: + priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case PRIORITY_LOW: + priority = THREAD_PRIORITY_LOWEST; + break; + case PRIORITY_IDLE: + priority = THREAD_PRIORITY_IDLE; + break; + default: + THROW("invalid priority %d", in_priority); + } + SetThreadPriority(GetCurrentThread(), priority); +} + +void Platform::set_event_listener(EventListener* listener) +{ + event_listener = listener ? listener : &default_event_listener; +} + +TimerID Platform::create_interval_timer(timer_proc_t proc, void* opaque) +{ + Timer* timer = alloc_timer(); + if (!timer) { + return INVALID_TIMER; + } + timer->proc = proc; + timer->opaque = opaque; + return timer->id; +} + +bool Platform::activate_interval_timer(TimerID timer, unsigned int millisec) +{ + if (timer >= NUM_TIMERS) { + return false; + } + + if (!SetTimer(paltform_win, timer + 1, millisec, NULL)) { + return false; + } + return true; +} + +bool Platform::deactivate_interval_timer(TimerID timer) +{ + if (timer >= NUM_TIMERS) { + return false; + } + KillTimer(paltform_win, timer + 1); + return true; +} + +void Platform::destroy_interval_timer(TimerID timer) +{ + if (timer == INVALID_TIMER) { + return; + } + ASSERT(timer < NUM_TIMERS); + KillTimer(paltform_win, timer + 1); + free_timer(&timers[timer]); +} + +uint64_t Platform::get_monolithic_time() +{ + return uint64_t(GetTickCount()) * 1000 * 1000; +} + +void Platform::get_temp_dir(std::string& path) +{ + DWORD len = GetTempPathA(0, NULL); + if (len <= 0) { + throw Exception("get temp patch failed"); + } + char* tmp_path = new char[len + 1]; + GetTempPathA(len, tmp_path); + path = tmp_path; + delete[] tmp_path; +} + +class WinMonitor: public Monitor { +public: + WinMonitor(int id, const wchar_t* name, const wchar_t* string); + + virtual void set_mode(int width, int height); + virtual void restore(); + virtual int get_depth() { return _depth;} + virtual Point get_position(); + virtual Point get_size() const { Point size = {_width, _height}; return size;} + virtual bool is_out_of_sync() { return _out_of_sync;} + virtual int get_screen_id() { return 0;} + +protected: + virtual ~WinMonitor(); + +private: + void update_position(); + bool change_display_settings(int width, int height, int depth); + bool best_display_setting(uint32_t width, uint32_t height, uint32_t depth); + +private: + std::wstring _dev_name; + std::wstring _dev_string; + bool _active; + Point _position; + int _width; + int _height; + int _depth; + bool _out_of_sync; +}; + +WinMonitor::WinMonitor(int id, const wchar_t* name, const wchar_t* string) + : Monitor(id) + , _dev_name (name) + , _dev_string (string) + , _active (false) + , _out_of_sync (false) +{ + update_position(); +} + +WinMonitor::~WinMonitor() +{ + restore(); +} + +void WinMonitor::update_position() +{ + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(_dev_name.c_str(), ENUM_CURRENT_SETTINGS, &mode); + _position.x = mode.dmPosition.x; + _position.y = mode.dmPosition.y; + _width = mode.dmPelsWidth; + _height = mode.dmPelsHeight; + _depth = mode.dmBitsPerPel; +} + +Point WinMonitor::get_position() +{ + update_position(); + return _position; +} + +bool WinMonitor::change_display_settings(int width, int height, int depth) +{ + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; + mode.dmPelsWidth = width; + mode.dmPelsHeight = height; + mode.dmBitsPerPel = depth; + + return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL) + == DISP_CHANGE_SUCCESSFUL; +} + +bool WinMonitor::best_display_setting(uint32_t width, uint32_t height, uint32_t depth) +{ + DEVMODE mode; + DWORD mode_id = 0; + uint32_t mod_waste = ~0; + DWORD mod_width; + DWORD mod_height; + DWORD mod_depth; + DWORD mod_frequency; + + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + while (EnumDisplaySettings(_dev_name.c_str(), mode_id++, &mode)) { + // Workaround for + // Lenovo T61p, Nvidia Quadro FX 570M and + // Lenovo T61, Nvidia Quadro NVS 140M + // + // with dual monitors configuration + // + // we get strange values from EnumDisplaySettings 640x480x4 frequency 1 + // and calling ChangeDisplaySettingsEx with that configuration result with + // machine that is stucked for a long period of time + if (mode.dmDisplayFrequency == 1) { + continue; + } + + if (mode.dmPelsWidth >= width && mode.dmPelsHeight >= height) { + bool replace = false; + uint32_t curr_waste = mode.dmPelsWidth * mode.dmPelsHeight - width * height; + if (curr_waste < mod_waste) { + replace = true; + } else if (curr_waste == mod_waste) { + if (mod_depth == mode.dmBitsPerPel) { + replace = mode.dmDisplayFrequency > mod_frequency; + } else if (mod_depth != depth && mode.dmBitsPerPel > mod_depth) { + replace = true; + } + } + if (replace) { + mod_waste = curr_waste; + mod_width = mode.dmPelsWidth; + mod_height = mode.dmPelsHeight; + mod_depth = mode.dmBitsPerPel; + mod_frequency = mode.dmDisplayFrequency; + } + } + } + if (mod_waste == ~0) { + return false; + } + mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + mode.dmPelsWidth = mod_width; + mode.dmPelsHeight = mod_height; + mode.dmBitsPerPel = mod_depth; + mode.dmDisplayFrequency = mod_frequency; + + return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL) + == DISP_CHANGE_SUCCESSFUL; +} + +void WinMonitor::set_mode(int width, int height) +{ + update_position(); + if (width == _width && height == _height) { + _out_of_sync = false; + return; + } + self_monitors_change++; + if (!change_display_settings(width, height, 32) && !best_display_setting(width, height, 32)) { + _out_of_sync = true; + } else { + _out_of_sync = false; + } + self_monitors_change--; + _active = true; + update_position(); +} + +void WinMonitor::restore() +{ + if (_active) { + _active = false; + self_monitors_change++; + ChangeDisplaySettingsEx(_dev_name.c_str(), NULL, NULL, 0, NULL); + self_monitors_change--; + } +} + +static MonitorsList monitors; + +const MonitorsList& Platform::init_monitors() +{ + ASSERT(monitors.empty()); + + int id = 0; + DISPLAY_DEVICE device_info; + DWORD device_id = 0; + device_info.cb = sizeof(device_info); + while (EnumDisplayDevices(NULL, device_id, &device_info, 0)) { + if ((device_info.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) && + !(device_info.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) { + monitors.push_back(new WinMonitor(id++, device_info.DeviceName, + device_info.DeviceString)); + } + device_id++; + } + return monitors; +} + +void Platform::destroy_monitors() +{ + while (!monitors.empty()) { + Monitor* monitor = monitors.front(); + monitors.pop_front(); + delete monitor; + } +} + +bool Platform::is_monitors_pos_valid() +{ + return true; +} + +void Platform::init() +{ + main_tread = GetCurrentThread(); + create_message_wind(); + init_timers(); +} + +WaveRecordAbstract* Platform::create_recorder(RecordClinet& client, + uint32_t sampels_per_sec, + uint32_t bits_per_sample, + uint32_t channels) +{ + return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels); +} + +WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec, + uint32_t bits_per_sample, + uint32_t channels) +{ + return new WavePlayer(sampels_per_sec, bits_per_sample, channels); +} + +static void toggle_modifier(int key) +{ + INPUT inputs[2]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].type = inputs[1].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = inputs[1].ki.wVk = key; + inputs[1].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(2, inputs, sizeof(INPUT)); +} + +uint32_t Platform::get_keyboard_modifiers() +{ + uint32_t modifiers = 0; + if ((GetKeyState(VK_SCROLL) & 1)) { + modifiers |= SCROLL_LOCK_MODIFIER; + } + if ((GetKeyState(VK_NUMLOCK) & 1)) { + modifiers |= NUM_LOCK_MODIFIER; + } + if ((GetKeyState(VK_CAPITAL) & 1)) { + modifiers |= CAPS_LOCK_MODIFIER; + } + return modifiers; +} + +void Platform::set_keyboard_modifiers(uint32_t modifiers) +{ + if (((modifiers >> SCROLL_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_SCROLL) & 1)) { + toggle_modifier(VK_SCROLL); + } + + if (((modifiers >> Platform::NUM_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_NUMLOCK) & 1)) { + toggle_modifier(VK_NUMLOCK); + } + + if (((modifiers >> CAPS_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_CAPITAL) & 1)) { + toggle_modifier(VK_CAPITAL); + } +} + +class WinBaseLocalCursor: public LocalCursor { +public: + WinBaseLocalCursor() : _handle (0) {} + void set(Window window) { SetCursor(_handle);} + +protected: + HCURSOR _handle; +}; + +class WinLocalCursor: public WinBaseLocalCursor { +public: + WinLocalCursor(CursorData* cursor_data); + ~WinLocalCursor(); + +private: + bool _shared; +}; + +WinLocalCursor::WinLocalCursor(CursorData* cursor_data) + : _shared (false) +{ + const CursorHeader& header = cursor_data->header(); + const uint8_t* data = cursor_data->data(); + int cur_size; + int bits = get_size_bits(header, cur_size); + if (!bits) { + THROW("invalid curosr type"); + } + if (header.type == CURSOR_TYPE_MONO) { + _handle = CreateCursor(NULL, header.hot_spot_x, header.hot_spot_y, + header.width, header.height, data, data + cur_size); + return; + } + ICONINFO icon; + icon.fIcon = FALSE; + icon.xHotspot = header.hot_spot_x; + icon.yHotspot = header.hot_spot_y; + icon.hbmColor = icon.hbmMask = NULL; + HDC hdc = GetDC(NULL); + + switch (header.type) { + case CURSOR_TYPE_ALPHA: + case CURSOR_TYPE_COLOR32: + case CURSOR_TYPE_COLOR16: { + BITMAPV5HEADER bmp_hdr; + ZeroMemory(&bmp_hdr, sizeof(bmp_hdr)); + bmp_hdr.bV5Size = sizeof(bmp_hdr); + bmp_hdr.bV5Width = header.width; + bmp_hdr.bV5Height = -header.height; + bmp_hdr.bV5Planes = 1; + bmp_hdr.bV5BitCount = bits; + bmp_hdr.bV5Compression = BI_BITFIELDS; + if (bits == 32) { + bmp_hdr.bV5RedMask = 0x00FF0000; + bmp_hdr.bV5GreenMask = 0x0000FF00; + bmp_hdr.bV5BlueMask = 0x000000FF; + } else if (bits == 16) { + bmp_hdr.bV5RedMask = 0x00007C00; + bmp_hdr.bV5GreenMask = 0x000003E0; + bmp_hdr.bV5BlueMask = 0x0000001F; + } + if (header.type == CURSOR_TYPE_ALPHA) { + bmp_hdr.bV5AlphaMask = 0xFF000000; + } + void* bmp_pixels = NULL; + icon.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *)&bmp_hdr, DIB_RGB_COLORS, &bmp_pixels, + NULL, 0); + memcpy(bmp_pixels, data, cur_size); + icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1, + (header.type == CURSOR_TYPE_ALPHA) ? NULL : + (CONST VOID *)(data + cur_size)); + break; + } + case CURSOR_TYPE_COLOR4: { + BITMAPINFO* bmp_info; + bmp_info = (BITMAPINFO *)new uint8_t[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) << bits)]; + ZeroMemory(bmp_info, sizeof(BITMAPINFO)); + bmp_info->bmiHeader.biSize = sizeof(bmp_info->bmiHeader); + bmp_info->bmiHeader.biWidth = header.width; + bmp_info->bmiHeader.biHeight = -header.height; + bmp_info->bmiHeader.biPlanes = 1; + bmp_info->bmiHeader.biBitCount = bits; + bmp_info->bmiHeader.biCompression = BI_RGB; + memcpy(bmp_info->bmiColors, data + cur_size, sizeof(RGBQUAD) << bits); + icon.hbmColor = CreateDIBitmap(hdc, &bmp_info->bmiHeader, CBM_INIT, data, + bmp_info, DIB_RGB_COLORS); + icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1, + (CONST VOID *)(data + cur_size + (sizeof(uint32_t) << bits))); + delete[] (uint8_t *)bmp_info; + break; + } + case CURSOR_TYPE_COLOR24: + case CURSOR_TYPE_COLOR8: + default: + LOG_WARN("unsupported cursor type %d", header.type); + _handle = LoadCursor(NULL, IDC_ARROW); + _shared = true; + ReleaseDC(NULL, hdc); + return; + } + + ReleaseDC(NULL, hdc); + + if (icon.hbmColor && icon.hbmMask) { + _handle = CreateIconIndirect(&icon); + } + if (icon.hbmMask) { + DeleteObject(icon.hbmMask); + } + if (icon.hbmColor) { + DeleteObject(icon.hbmColor); + } +} + +WinLocalCursor::~WinLocalCursor() +{ + if (_handle && !_shared) { + DestroyCursor(_handle); + } +} + +LocalCursor* Platform::create_local_cursor(CursorData* cursor_data) +{ + return new WinLocalCursor(cursor_data); +} + +class WinInactiveCursor: public WinBaseLocalCursor { +public: + WinInactiveCursor() { _handle = LoadCursor(NULL, IDC_NO);} +}; + +LocalCursor* Platform::create_inactive_cursor() +{ + return new WinInactiveCursor(); +} + +class WinDefaultCursor: public WinBaseLocalCursor { +public: + WinDefaultCursor() { _handle = LoadCursor(NULL, IDC_ARROW);} +}; + +LocalCursor* Platform::create_default_cursor() +{ + return new WinDefaultCursor(); +} + +void Platform::set_display_mode_listner(DisplayModeListner* listener) +{ +} + +Icon* Platform::load_icon(int id) +{ + HICON icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(id)); + if (!icon) { + return NULL; + } + return new WinIcon(icon); +} + |