From c1b79eb035fa158fb2ac3bc8e559809611070016 Mon Sep 17 00:00:00 2001 From: Yaniv Kamay Date: Sat, 19 Sep 2009 21:25:46 +0300 Subject: fresh start --- client/windows/atomic_count.h | 42 ++ client/windows/events_loop_p.cpp | 158 ++++++ client/windows/events_loop_p.h | 42 ++ client/windows/main.cpp | 124 +++++ client/windows/my_getopt.cpp | 20 + client/windows/named_pipe.cpp | 227 +++++++++ client/windows/named_pipe.h | 94 ++++ client/windows/pixels_source.cpp | 108 +++++ client/windows/pixels_source_p.h | 29 ++ client/windows/platform.cpp | 786 ++++++++++++++++++++++++++++++ client/windows/platform_utils.cpp | 151 ++++++ client/windows/platform_utils.h | 84 ++++ client/windows/playback.cpp | 177 +++++++ client/windows/playback.h | 55 +++ client/windows/record.cpp | 193 ++++++++ client/windows/record.h | 58 +++ client/windows/red_drawable.cpp | 151 ++++++ client/windows/red_pixmap.cpp | 42 ++ client/windows/red_pixmap_cairo.cpp | 101 ++++ client/windows/red_pixmap_gdi.cpp | 102 ++++ client/windows/red_window.cpp | 933 ++++++++++++++++++++++++++++++++++++ client/windows/red_window_p.h | 68 +++ client/windows/redc.rc | 138 ++++++ client/windows/redc.sln | 20 + client/windows/redc.vcproj | 597 +++++++++++++++++++++++ client/windows/resource.h | 19 + client/windows/solideice.ico | Bin 0 -> 15342 bytes client/windows/spicec.exe.manifest | 10 + client/windows/splash.bmp | Bin 0 -> 921656 bytes client/windows/static_title.bmp | Bin 0 -> 29756 bytes client/windows/stdint.h | 394 +++++++++++++++ client/windows/win_platform.h | 50 ++ 32 files changed, 4973 insertions(+) create mode 100644 client/windows/atomic_count.h create mode 100644 client/windows/events_loop_p.cpp create mode 100644 client/windows/events_loop_p.h create mode 100644 client/windows/main.cpp create mode 100644 client/windows/my_getopt.cpp create mode 100644 client/windows/named_pipe.cpp create mode 100644 client/windows/named_pipe.h create mode 100644 client/windows/pixels_source.cpp create mode 100644 client/windows/pixels_source_p.h create mode 100644 client/windows/platform.cpp create mode 100644 client/windows/platform_utils.cpp create mode 100644 client/windows/platform_utils.h create mode 100644 client/windows/playback.cpp create mode 100644 client/windows/playback.h create mode 100644 client/windows/record.cpp create mode 100644 client/windows/record.h create mode 100644 client/windows/red_drawable.cpp create mode 100644 client/windows/red_pixmap.cpp create mode 100644 client/windows/red_pixmap_cairo.cpp create mode 100644 client/windows/red_pixmap_gdi.cpp create mode 100644 client/windows/red_window.cpp create mode 100644 client/windows/red_window_p.h create mode 100644 client/windows/redc.rc create mode 100644 client/windows/redc.sln create mode 100644 client/windows/redc.vcproj create mode 100644 client/windows/resource.h create mode 100644 client/windows/solideice.ico create mode 100644 client/windows/spicec.exe.manifest create mode 100644 client/windows/splash.bmp create mode 100644 client/windows/static_title.bmp create mode 100644 client/windows/stdint.h create mode 100644 client/windows/win_platform.h (limited to 'client/windows') diff --git a/client/windows/atomic_count.h b/client/windows/atomic_count.h new file mode 100644 index 00000000..d7a057a3 --- /dev/null +++ b/client/windows/atomic_count.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#ifndef _H_ATOMIC_COUNT +#define _H_ATOMIC_COUNT + +class AtomicCount { +public: + AtomicCount(uint32_t count = 0) : _count (count) {} + + uint32_t operator ++ () + { + return InterlockedIncrement(&_count); + } + + uint32_t operator -- () + { + return InterlockedDecrement(&_count); + } + + operator uint32_t () { return _count;} + +private: + LONG _count; +}; + +#endif + diff --git a/client/windows/events_loop_p.cpp b/client/windows/events_loop_p.cpp new file mode 100644 index 00000000..7329d161 --- /dev/null +++ b/client/windows/events_loop_p.cpp @@ -0,0 +1,158 @@ +/* + 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 . +*/ + +#include "common.h" +#include "events_loop.h" +#include "debug.h" +#include "utils.h" + + +EventsLoop::EventsLoop() +{ +} + +EventsLoop::~EventsLoop() +{ +} + +void EventsLoop::run() +{ + for (;;) { + run_once(); + } +} + +void EventsLoop::run_once(int timeout_milli) +{ + DWORD wait_res = WaitForMultipleObjects(_handles.size(), &_handles[0], FALSE, timeout_milli); + if (wait_res == WAIT_FAILED) { + THROW("wait failed"); + } + int event_index = wait_res - WAIT_OBJECT_0; + if (event_index < 0 || event_index >= (int)_events.size()) { + THROW("invalid event id"); + } + _events[event_index]->action(); +} + +void EventsLoop::add_socket(Socket& socket) +{ + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!event) { + THROW("create event failed"); + } + if (WSAEventSelect(socket.get_socket(), event, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR) { + CloseHandle(event); + THROW("event select failed"); + } + int size = _events.size(); + _events.resize(size + 1); + _handles.resize(size + 1); + _events[size] = &socket; + _handles[size] = event; +} + +void EventsLoop::remove_socket(Socket& socket) +{ + int size = _events.size(); + for (int i = 0; i < size; i++) { + if (_events[i] == &socket) { + if (WSAEventSelect(socket.get_socket(), NULL, 0) == SOCKET_ERROR) { + THROW("event select failed"); + } + u_long arg = 0; + if (ioctlsocket(socket.get_socket(), FIONBIO, &arg) == SOCKET_ERROR) { + THROW("set blocking mode failed"); + } + CloseHandle(_handles[i]); + for (i++; i < size; i++) { + _events[i - 1] = _events[i]; + _handles[i - 1] = _handles[i]; + } + _events.resize(size - 1); + _handles.resize(size - 1); + return; + } + } + THROW("socket not found"); +} + +void EventsLoop::add_trigger(Trigger& trigger) +{ + int size = _events.size(); + _events.resize(size + 1); + _handles.resize(size + 1); + _events[size] = &trigger; + _handles[size] = trigger.get_handle(); +} + +void EventsLoop::remove_trigger(Trigger& trigger) +{ + int size = _events.size(); + for (int i = 0; i < size; i++) { + if (_events[i] == &trigger) { + for (i++; i < size; i++) { + _events[i - 1] = _events[i]; + _handles[i - 1] = _handles[i]; + } + _events.resize(size - 1); + _handles.resize(size - 1); + return; + } + } + THROW("trigger not found"); +} + +EventsLoop::Trigger::Trigger() +{ + if (!(event = CreateEvent(NULL, FALSE, FALSE, NULL))) { + THROW("create event failed"); + } +} + +EventsLoop::Trigger::~Trigger() +{ + CloseHandle(event); +} + +void EventsLoop::Trigger::trigger() +{ + if (!SetEvent(event)) { + THROW("set event failed"); + } +} + +void EventsLoop::Trigger::reset() +{ + if (!ResetEvent(event)) { + THROW("set event failed"); + } +} + +void EventsLoop::Trigger::action() +{ + on_event(); +} + +void EventsLoop::add_file(File& file) +{ +} + +void EventsLoop::remove_file(File& file) +{ +} + diff --git a/client/windows/events_loop_p.h b/client/windows/events_loop_p.h new file mode 100644 index 00000000..8361398d --- /dev/null +++ b/client/windows/events_loop_p.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#ifndef _H_EVENTS_LOOP_P +#define _H_EVENTS_LOOP_P + +#include "common.h" + +#include + +class EventSource; + +class EventsLoop_p { +public: + std::vector _events; + std::vector _handles; +}; + +class Trigger_p { +public: + HANDLE get_handle() { return event;} + +public: + HANDLE event; +}; + +#endif + diff --git a/client/windows/main.cpp b/client/windows/main.cpp new file mode 100644 index 00000000..a1575e90 --- /dev/null +++ b/client/windows/main.cpp @@ -0,0 +1,124 @@ +/* + 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 . +*/ + +#include "common.h" +#include +#include +extern "C" { +#include "pthread.h" +} + +//#define OPEN_CONSOLE +#ifdef OPEN_CONSOLE +#include +#include +#endif + +#include "application.h" +#include "debug.h" +#include "utils.h" + +HINSTANCE instance = NULL; + +static void init_winsock() +{ + WSADATA wsaData; + int res; + + if ((res = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) { + THROW("WSAStartup failed %d", res); + } +} + +char* version_string = "???"; +static char _version_string[40]; + +static void init_version_string() +{ + DWORD handle; + DWORD verrsion_inf_size = GetFileVersionInfoSizeA(__argv[0], &handle); + if (verrsion_inf_size == 0) { + return; + } + AutoArray info_buf (new uint8_t[verrsion_inf_size]); + if (!GetFileVersionInfoA(__argv[0], handle, verrsion_inf_size, info_buf.get())) { + return; + } + UINT size; + VS_FIXEDFILEINFO *file_info; + if (!VerQueryValueA(info_buf.get(), "\\", (VOID**)&file_info, &size) || + size < sizeof(VS_FIXEDFILEINFO)) { + return; + } + sprintf(_version_string, "%d.%d.%d.%d", + file_info->dwFileVersionMS >> 16, + file_info->dwFileVersionMS & 0x0ffff, + file_info->dwFileVersionLS >> 16, + file_info->dwFileVersionLS & 0x0ffff); + version_string = _version_string; +} + +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + int exit_val; + + instance = hInstance; + + try { + init_version_string(); +#ifdef OPEN_CONSOLE + AllocConsole(); + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + int hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT); + FILE * fp = _fdopen(hConHandle, "w"); + *stdout = *fp; + + h = GetStdHandle(STD_INPUT_HANDLE); + hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT); + fp = _fdopen(hConHandle, "r"); + *stdin = *fp; + + h = GetStdHandle(STD_ERROR_HANDLE); + hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT); + fp = _fdopen(hConHandle, "w"); + *stderr = *fp; +#endif + pthread_win32_process_attach_np(); + init_winsock(); + exit_val = Application::main(__argc, __argv, version_string); + LOG_INFO("Spice client terminated (exitcode = %d)", exit_val); + } catch (Exception& e) { + LOG_ERROR("unhandle exception: %s", e.what()); + exit_val = e.get_error_code(); + } catch (std::exception& e) { + LOG_ERROR("unhandle exception: %s", e.what()); + exit_val = SPICEC_ERROR_CODE_ERROR; + } catch (...) { + LOG_ERROR("unhandled exception"); + exit_val = SPICEC_ERROR_CODE_ERROR; + } + log4cpp::Category::shutdown(); +#ifdef OPEN_CONSOLE + _getch(); +#endif + pthread_win32_process_detach_np(); + return exit_val; +} + diff --git a/client/windows/my_getopt.cpp b/client/windows/my_getopt.cpp new file mode 100644 index 00000000..228dc006 --- /dev/null +++ b/client/windows/my_getopt.cpp @@ -0,0 +1,20 @@ +/* + 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 . +*/ + +#include "common.h" +#include "../../common/win/my_getopt-1.5/my_getopt.c" + diff --git a/client/windows/named_pipe.cpp b/client/windows/named_pipe.cpp new file mode 100644 index 00000000..f33c476e --- /dev/null +++ b/client/windows/named_pipe.cpp @@ -0,0 +1,227 @@ +/* + 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 . +*/ + +#include "common.h" +#include "named_pipe.h" +#include "utils.h" +#include "debug.h" + +PipeBuffer::PipeBuffer(HANDLE pipe) + : _handler (NULL) + , _pipe (pipe) + , _start (0) + , _end (0) + , _pending (false) +{ + ZeroMemory(&_overlap, sizeof(_overlap)); + _overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + _event_handle = _overlap.hEvent; + WinPlatform::add_event(*this); +} + +PipeBuffer::~PipeBuffer() +{ + WinPlatform::remove_event(*this); + CloseHandle(_event_handle); +} + +DWORD PipeBuffer::get_overlapped_bytes() +{ + DWORD bytes = 0; + + if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE) || bytes == 0) { + _pending = false; + _handler->on_data(); + } + return bytes; +} + +int32_t PipeReader::read(uint8_t *buf, int32_t size) +{ + ASSERT(buf && size >= 0); + + if (_start < _end) { + int32_t bytes_read = 0; + bytes_read = MIN(_end - _start, (uint32_t)size); + CopyMemory(buf, _data + _start, bytes_read); + _start += bytes_read; + if (_start == _end) { + _start = _end = 0; + } + return bytes_read; + } + if (_pending) { + return 0; + } + if (!ReadFile(_pipe, _data + _end, sizeof(_data) - _end, NULL, &_overlap) && + GetLastError() != ERROR_IO_PENDING) { + DBG(0, "ReadFile() failed %u", GetLastError()); + return -1; + } + _pending = true; + return 0; +} + +void PipeReader::on_event() +{ + ASSERT(_pending); + DWORD bytes = get_overlapped_bytes(); + + if (!bytes) { + return; + } + _end += bytes; + _pending = false; + _handler->on_data(); +} + +int32_t PipeWriter::write(const uint8_t *buf, int32_t size) +{ + int32_t bytes_written = 0; + ASSERT(buf && size >= 0); + + if (!_pending && _start == _end) { + _start = _end = 0; + } + if (_end < sizeof(_data)) { + bytes_written = MIN(sizeof(_data) - _end, (uint32_t)size); + CopyMemory(_data + _end, buf, bytes_written); + _end += bytes_written; + } + if (!_pending && _start < _end) { + if (!WriteFile(_pipe, _data + _start, _end - _start, NULL, &_overlap) && + GetLastError() != ERROR_IO_PENDING) { + DBG(0, "WriteFile() failed %u", GetLastError()); + return -1; + } + _pending = true; + } + return bytes_written; +} + +void PipeWriter::on_event() +{ + ASSERT(_pending); + DWORD bytes = get_overlapped_bytes(); + if (!bytes) { + return; + } + _start += bytes; + _pending = false; + if (_start == sizeof(_data)) { + _handler->on_data(); + } +} + +WinConnection::WinConnection(HANDLE pipe) + : _pipe (pipe) + , _writer (pipe) + , _reader (pipe) +{ +} + +WinConnection::~WinConnection() +{ + if (!DisconnectNamedPipe(_pipe)) { + DBG(0, "DisconnectNamedPipe failed %d", GetLastError()); + } + CloseHandle(_pipe); +} + +int32_t WinConnection::read(uint8_t *buf, int32_t size) +{ + return _reader.read(buf, size); +} + +int32_t WinConnection::write(const uint8_t *buf, int32_t size) +{ + return _writer.write(buf, size); +} + +void WinConnection::set_handler(NamedPipe::ConnectionInterface* handler) +{ + _reader.set_handler(handler); + _writer.set_handler(handler); +} + +WinListener::WinListener(const char *name, NamedPipe::ListenerInterface &listener_interface) + : _listener_interface (listener_interface) + , _pipe (0) +{ + _pipename = new TCHAR[PIPE_MAX_NAME_LEN]; + swprintf_s(_pipename, PIPE_MAX_NAME_LEN, L"%s%S", PIPE_PREFIX, name); + ZeroMemory(&_overlap, sizeof(_overlap)); + _overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + _event_handle = _overlap.hEvent; + WinPlatform::add_event(*this); + create_pipe(); +} + +WinListener::~WinListener() +{ + CancelIo(_pipe); + WinPlatform::remove_event(*this); + CloseHandle(_event_handle); + delete[] _pipename; +} + +void WinListener::on_event() +{ + DWORD bytes; + + if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE)) { + DBG(0, "GetOverlappedResult() failed %u", GetLastError()); + return; + } + DBG(0, "Pipe connected 0x%p", _pipe); + WinConnection *con = new WinConnection(_pipe); + NamedPipe::ConnectionInterface &con_interface = _listener_interface.create(); + con->set_handler(&con_interface); + con_interface.bind((NamedPipe::ConnectionRef)con); + create_pipe(); +} + +void WinListener::create_pipe() +{ + _pipe = CreateNamedPipe(_pipename, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, PIPE_BUF_SIZE, PIPE_BUF_SIZE, + PIPE_TIMEOUT, NULL); + if (_pipe == INVALID_HANDLE_VALUE) { + THROW("CreateNamedPipe() failed %u", GetLastError()); + } + if (ConnectNamedPipe(_pipe, &_overlap)) { + THROW("ConnectNamedPipe() is not pending"); + } + switch (GetLastError()) { + case ERROR_IO_PENDING: + DBG(0, "Pipe waits for connection"); + break; + case ERROR_PIPE_CONNECTED: { + DBG(0, "Pipe already connected"); + WinConnection *con = new WinConnection(_pipe); + NamedPipe::ConnectionInterface &con_interface = _listener_interface.create(); + con->set_handler(&con_interface); + con_interface.bind((NamedPipe::ConnectionRef)con); + create_pipe(); + break; + } + default: + THROW("ConnectNamedPipe() failed %u", GetLastError()); + } +} + diff --git a/client/windows/named_pipe.h b/client/windows/named_pipe.h new file mode 100644 index 00000000..578c34d2 --- /dev/null +++ b/client/windows/named_pipe.h @@ -0,0 +1,94 @@ +/* + 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 . +*/ + +#ifndef _H_NAMED_PIPE +#define _H_NAMED_PIPE + +#include +#include "platform.h" +#include "win_platform.h" + +#define PIPE_TIMEOUT 5000 +#define PIPE_BUF_SIZE 8192 +#define PIPE_MAX_NAME_LEN 256 +#define PIPE_PREFIX TEXT("\\\\.\\pipe\\") + +class WinConnection; + +class PipeBuffer: public EventOwner { +public: + PipeBuffer(HANDLE pipe); + ~PipeBuffer(); + void set_handler(NamedPipe::ConnectionInterface* handler) { _handler = handler;} + DWORD get_overlapped_bytes(); + +protected: + NamedPipe::ConnectionInterface *_handler; + OVERLAPPED _overlap; + HANDLE _pipe; + uint32_t _start; + uint32_t _end; + uint8_t _data[PIPE_BUF_SIZE]; + bool _pending; +}; + +class PipeReader: public PipeBuffer { +public: + PipeReader(HANDLE pipe) : PipeBuffer(pipe) {} + int32_t read(uint8_t *buf, int32_t size); + void on_event(); +}; + +class PipeWriter: public PipeBuffer { +public: + PipeWriter(HANDLE pipe) : PipeBuffer(pipe) {} + int32_t write(const uint8_t *buf, int32_t size); + void on_event(); +}; + +class WinConnection { +public: + WinConnection(HANDLE pipe); + ~WinConnection(); + int32_t read(uint8_t *buf, int32_t size); + int32_t write(const uint8_t *buf, int32_t size); + void set_handler(NamedPipe::ConnectionInterface* handler); + +private: + HANDLE _pipe; + PipeWriter _writer; + PipeReader _reader; +}; + +class WinListener: public EventOwner { +public: + WinListener(const char *name, NamedPipe::ListenerInterface &listener_interface); + ~WinListener(); + void on_event(); + +private: + void create_pipe(); + +private: + TCHAR *_pipename; + NamedPipe::ListenerInterface &_listener_interface; + OVERLAPPED _overlap; + HANDLE _pipe; +}; + +#endif + diff --git a/client/windows/pixels_source.cpp b/client/windows/pixels_source.cpp new file mode 100644 index 00000000..6d210f09 --- /dev/null +++ b/client/windows/pixels_source.cpp @@ -0,0 +1,108 @@ +/* + 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 . +*/ + +#include "common.h" +#include "pixels_source.h" +#include "pixels_source_p.h" +#include "platform_utils.h" +#include "threads.h" +#include "debug.h" + + +static Point get_bitmap_size(HDC dc) +{ + BITMAP bitmap_info; + Point size; + + GetObject(GetCurrentObject(dc, OBJ_BITMAP), sizeof(bitmap_info), &bitmap_info); + + size.x = bitmap_info.bmWidth; + size.y = bitmap_info.bmHeight; + return size; +} + +PixelsSource::PixelsSource() +{ + ASSERT(sizeof(_opaque) >= sizeof(PixelsSource_p)); + _origin.x = _origin.y = 0; + memset(_opaque, 0, sizeof(_opaque)); + PixelsSource_p* p_data = (PixelsSource_p*)_opaque; + p_data->_mutex = new Mutex(); +} + +PixelsSource::~PixelsSource() +{ + PixelsSource_p* p_data = (PixelsSource_p*)_opaque; + delete p_data->_mutex; +} + +struct ResImage_p { + PixelsSource_p source_p; + HBITMAP prev_bitmap; +}; + + +ImageFromRes::ImageFromRes(int res_id) +{ + AutoDC dc(create_compatible_dc()); + ((ResImage_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(), + get_bitmap_res(res_id)); + ((ResImage_p*)get_opaque())->source_p.dc = dc.release(); +} + +ImageFromRes::~ImageFromRes() +{ + HDC dc = ((ResImage_p*)get_opaque())->source_p.dc; + if (dc) { + HGDIOBJ bitmap = SelectObject(dc, ((ResImage_p*)get_opaque())->prev_bitmap); + DeleteObject(bitmap); + DeleteDC(dc); + } +} + +Point ImageFromRes::get_size() +{ + ResImage_p* p_data = (ResImage_p*)get_opaque(); + Lock lock(*p_data->source_p._mutex); + return get_bitmap_size(p_data->source_p.dc); +} + +AlphaImageFromRes::AlphaImageFromRes(int res_id) +{ + AutoDC dc(create_compatible_dc()); + ((ResImage_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(), + get_alpha_bitmap_res(res_id)); + ((ResImage_p*)get_opaque())->source_p.dc = dc.release(); +} + +AlphaImageFromRes::~AlphaImageFromRes() +{ + HDC dc = ((ResImage_p*)get_opaque())->source_p.dc; + if (dc) { + HGDIOBJ bitmap = SelectObject(dc, ((ResImage_p*)get_opaque())->prev_bitmap); + DeleteObject(bitmap); + DeleteDC(dc); + } +} + +Point AlphaImageFromRes::get_size() +{ + ResImage_p* p_data = (ResImage_p*)get_opaque(); + Lock lock(*p_data->source_p._mutex); + return get_bitmap_size(p_data->source_p.dc); +} + diff --git a/client/windows/pixels_source_p.h b/client/windows/pixels_source_p.h new file mode 100644 index 00000000..eb646395 --- /dev/null +++ b/client/windows/pixels_source_p.h @@ -0,0 +1,29 @@ +/* + 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 . +*/ + +#ifndef _H_PIXELE_SOURSR_P +#define _H_PIXELE_SOURSR_P + +class Mutex; + +struct PixelsSource_p { + HDC dc; + Mutex* _mutex; +}; + +#endif + 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 . +*/ + +#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 events; +static std::vector 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); +} + diff --git a/client/windows/platform_utils.cpp b/client/windows/platform_utils.cpp new file mode 100644 index 00000000..6b9049a0 --- /dev/null +++ b/client/windows/platform_utils.cpp @@ -0,0 +1,151 @@ +/* + 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 . +*/ + +#include "common.h" +#include +#include "platform_utils.h" +#include "utils.h" +#include "threads.h" + +void string_vprintf(std::string& str, const char* format, va_list ap) +{ + int buf_size = 256; + for (;;) { + AutoArray buf(new char[buf_size]); + int r = vsnprintf_s(buf.get(), buf_size, buf_size - 1, format, ap); + if (r != -1) { + str = buf.get(); + return; + } + buf_size *= 2; + } +} + +void wstring_vprintf(std::wstring& str, const wchar_t* format, va_list ap) +{ + int buf_size = 256; + for (;;) { + AutoArray buf(new wchar_t[buf_size]); + int r = vswprintf(buf.get(), buf_size, format, ap); + if (r != -1) { + str = buf.get(); + return; + } + buf_size *= 2; + } +} + +HDC create_compatible_dc() +{ + HDC dc = CreateCompatibleDC(NULL); + if (!dc) { + THROW("create compatible DC failed"); + } + return dc; +} + +HBITMAP get_bitmap_res(int id) +{ + HBITMAP bitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(id)); + if (!bitmap) { + THROW("get bitmpa #%d failed", id); + } + return bitmap; +} + +HBITMAP get_alpha_bitmap_res(int id) +{ + AutoGDIObject bitmap(LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(id), IMAGE_BITMAP, 0, 0, + LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_SHARED)); + if (!bitmap.valid()) { + THROW("get bitmpa #%d failed", id); + } + + BITMAP src_info; + GetObject(bitmap.get(), sizeof(src_info), &src_info); + if (src_info.bmBitsPixel != 32 || src_info.bmPlanes != 1) { + THROW("invalid format #%d ", id); + } + + LONG src_size = src_info.bmHeight * src_info.bmWidthBytes; + AutoArray src_pixels(new uint8_t[src_size]); + LONG ncopy = GetBitmapBits((HBITMAP)bitmap.get(), src_size, src_pixels.get()); + if (ncopy != src_size) { + THROW("get vitmap bits failed, %u", GetLastError()); + } + + AutoDC auto_dc(create_compatible_dc()); + BITMAPINFO dest_info; + uint8_t *dest; + dest_info.bmiHeader.biSize = sizeof(dest_info.bmiHeader); + dest_info.bmiHeader.biWidth = src_info.bmWidth; + dest_info.bmiHeader.biHeight = -src_info.bmHeight; + dest_info.bmiHeader.biPlanes = 1; + dest_info.bmiHeader.biBitCount = 32; + dest_info.bmiHeader.biCompression = BI_RGB; + dest_info.bmiHeader.biSizeImage = 0; + dest_info.bmiHeader.biXPelsPerMeter = dest_info.bmiHeader.biYPelsPerMeter = 0; + dest_info.bmiHeader.biClrUsed = 0; + dest_info.bmiHeader.biClrImportant = 0; + + HBITMAP ret = CreateDIBSection(auto_dc.get(), &dest_info, 0, (VOID **)&dest, NULL, 0); + if (!ret) { + THROW("create bitmap failed, %u", GetLastError()); + } + + uint8_t* src_line = src_pixels.get(); + for (int i = 0; i < src_info.bmHeight; i++, src_line += src_info.bmWidthBytes) { + uint8_t* src = src_line; + for (int j = 0; j < src_info.bmWidth; j++) { + dest[3] = src[3]; + double alpha = (double)dest[3] / 0xff; + dest[2] = (uint8_t)(alpha * src[2]); + dest[1] = (uint8_t)(alpha * src[1]); + dest[0] = (uint8_t)(alpha * src[0]); + src += 4; + dest += 4; + } + } + return ret; +} + +static std::map errors_map; +static Mutex errors_map_mutex; + +const char* sys_err_to_str(int error) +{ + Lock lock(errors_map_mutex); + if (errors_map.find(error) == errors_map.end()) { + LPSTR msg; + if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&msg, 0, NULL)) { + const int BUF_SIZE = 20; + msg = new char[BUF_SIZE]; + _snprintf(msg, BUF_SIZE, "errno %d", error); + } else { + char *new_line; + if ((new_line = strrchr(msg, '\r'))) { + *new_line = 0; + } + } + errors_map[error] = msg; + } + return errors_map[error]; +} + diff --git a/client/windows/platform_utils.h b/client/windows/platform_utils.h new file mode 100644 index 00000000..95b04fcb --- /dev/null +++ b/client/windows/platform_utils.h @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +#ifndef _H_PLATFORM_UTILS +#define _H_PLATFORM_UTILS + +#define mb() __asm {lock add [esp], 0} + +template, T invalid = NULL > +class AutoRes { +public: + AutoRes() : res (invalid) {} + AutoRes(T inRes) : res (inRes) {} + ~AutoRes() { set(invalid); } + + void set(T inRes) {if (res != invalid) free_res(res); res = inRes; } + T get() {return res;} + T release() {T tmp = res; res = invalid; return tmp; } + bool valid() { return res != invalid; } + +private: + AutoRes(const AutoRes&); + AutoRes& operator = (const AutoRes&); + +private: + T res; + FreeRes free_res; +}; + +class Delete_DC { +public: + void operator () (HDC dc) { DeleteDC(dc);} +}; + +typedef AutoRes AutoDC; + +class Delete_Object { +public: + void operator () (HGDIOBJ obj) { DeleteObject(obj);} +}; +typedef AutoRes AutoGDIObject; + +class DeleteOGLContext { +public: + void operator () (HGLRC ctx) { wglDeleteContext(ctx);} +}; + +typedef AutoRes AutoOGLCtx; + +HDC create_compatible_dc(); +HBITMAP get_bitmap_res(int id); +HBITMAP get_alpha_bitmap_res(int id); + +class WindowDC { +public: + WindowDC(HWND window) : _window (window), _dc (GetDC(window)) {} + ~WindowDC() { ReleaseDC(_window, _dc);} + HDC operator * () { return _dc;} + +private: + HWND _window; + HDC _dc; +}; + +typedef AutoRes AutoReleaseDC; + +const char* sys_err_to_str(int error); + +#endif + diff --git a/client/windows/playback.cpp b/client/windows/playback.cpp new file mode 100644 index 00000000..2e15ad93 --- /dev/null +++ b/client/windows/playback.cpp @@ -0,0 +1,177 @@ +/* + 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 . +*/ + +#include "common.h" +#include "playback.h" +#include "utils.h" +#include "debug.h" + + +#define RING_SIZE_MS 160 +#define START_MARK_MS 80 +#define LOW_MARK_MS 40 + + +WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels) + : _wave_out (NULL) + , _sampels_per_ms (sampels_per_sec / 1000) + , _ring (NULL) + , _head (0) + , _in_use (0) + , _paused (true) +{ + WAVEFORMATEX info; + uint32_t sample_bytes; + + info.wFormatTag = WAVE_FORMAT_PCM; + info.nChannels = channels; + info.nSamplesPerSec = sampels_per_sec; + sample_bytes = info.nBlockAlign = channels * bits_per_sample / 8; + info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign; + info.wBitsPerSample = bits_per_sample; + + if (waveOutOpen(&_wave_out, WAVE_MAPPER, &info, NULL, NULL, CALLBACK_NULL) + != MMSYSERR_NOERROR) { + throw Exception("can not open playback device"); + } + + int frame_size = WavePlaybackAbstract::FRAME_SIZE; + _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size; + int low_mark = (sampels_per_sec * LOW_MARK_MS / 1000) / frame_size; + _start_mark = (sampels_per_sec * START_MARK_MS / 1000) / frame_size; + _frame_bytes = frame_size * channels * bits_per_sample / 8; + _ring_item_size = sizeof(WAVEHDR) + _frame_bytes + sample_bytes; + + try { + _ring = new uint8_t[_ring_size * _ring_item_size]; + init_ring(sample_bytes); + } catch (...) { + delete[] _ring; + waveOutClose(_wave_out); + throw; + } + waveOutPause(_wave_out); +} + +WavePlayer::~WavePlayer() +{ + waveOutReset(_wave_out); + reclaim(); + waveOutClose(_wave_out); + delete[] _ring; +} + +void WavePlayer::init_ring(uint32_t sample_bytes) +{ + uint8_t* ptr = _ring; + uint8_t* end = ptr + _ring_size * _ring_item_size; + for (; ptr != end; ptr += _ring_item_size) { + WAVEHDR* buf = (WAVEHDR*)ptr; + memset(ptr, 0, _ring_item_size); + buf->dwBufferLength = _frame_bytes; + ULONG_PTR ptr = (ULONG_PTR)(buf + 1); + ptr = (ptr + sample_bytes - 1) / sample_bytes * sample_bytes; + buf->lpData = (LPSTR)ptr; + } +} + +inline WAVEHDR* WavePlayer::wave_hader(uint32_t position) +{ + ASSERT(position < _ring_size); + return (WAVEHDR*)(_ring + position * _ring_item_size); +} + +inline void WavePlayer::move_head() +{ + _head = (_head + 1) % _ring_size; + _in_use--; +} + +void WavePlayer::reclaim() +{ + while (_in_use) { + WAVEHDR* front_buf = wave_hader(_head); + if (!(front_buf->dwFlags & WHDR_DONE)) { + break; + } + MMRESULT err = waveOutUnprepareHeader(_wave_out, front_buf, sizeof(WAVEHDR)); + if (err != MMSYSERR_NOERROR) { + LOG_WARN("waveOutUnprepareHeader failed %u", err); + } + front_buf->dwFlags &= ~WHDR_DONE; + move_head(); + } +} + +WAVEHDR* WavePlayer::get_buff() +{ + reclaim(); + if (_in_use == _ring_size) { + return NULL; + } + + WAVEHDR* buff = wave_hader((_head + _in_use) % _ring_size); + ++_in_use; + return buff; +} + +bool WavePlayer::write(uint8_t* frame) +{ + WAVEHDR* buff = get_buff(); + if (buff) { + memcpy(buff->lpData, frame, _frame_bytes); + MMRESULT err; + err = waveOutPrepareHeader(_wave_out, buff, sizeof(WAVEHDR)); + + if (err != MMSYSERR_NOERROR) { + THROW("waveOutPrepareHeader filed %d", err); + } + err = waveOutWrite(_wave_out, buff, sizeof(WAVEHDR)); + if (err != MMSYSERR_NOERROR) { + THROW("waveOutWrite filed %d", err); + } + } + if (_paused && _in_use == _start_mark) { + _paused = false; + waveOutRestart(_wave_out); + } + return true; +} + +void WavePlayer::drain() +{ +} + +void WavePlayer::stop() +{ + drain(); + waveOutReset(_wave_out); + waveOutPause(_wave_out); + _paused = true; + reclaim(); +} + +bool WavePlayer::abort() +{ + return true; +} + +uint32_t WavePlayer::get_delay_ms() +{ + return _in_use * WavePlaybackAbstract::FRAME_SIZE / _sampels_per_ms; +} + diff --git a/client/windows/playback.h b/client/windows/playback.h new file mode 100644 index 00000000..9a50cdba --- /dev/null +++ b/client/windows/playback.h @@ -0,0 +1,55 @@ +/* + 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 . +*/ + +#ifndef _H_WINDOWS_AUDIO_PLAYBACK +#define _H_WINDOWS_AUDIO_PLAYBACK + +#include "audio_devices.h" + +class WavePlayer: public WavePlaybackAbstract { +public: + WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels); + virtual ~WavePlayer(); + + virtual bool write(uint8_t* frame); + virtual bool abort(); + virtual void stop(); + virtual uint32_t get_delay_ms(); + +private: + WAVEHDR* wave_hader(uint32_t position); + void move_head(); + WAVEHDR* get_buff(); + void reclaim(); + void drain(); + void init_ring(uint32_t sample_bytes); + +private: + HWAVEOUT _wave_out; + uint32_t _sampels_per_ms; + uint32_t _frame_bytes; + uint32_t _start_mark; + uint32_t _ring_item_size; + uint8_t* _ring; + uint32_t _ring_size; + uint32_t _head; + uint32_t _in_use; + bool _paused; +}; + +#endif + diff --git a/client/windows/record.cpp b/client/windows/record.cpp new file mode 100644 index 00000000..52fc478e --- /dev/null +++ b/client/windows/record.cpp @@ -0,0 +1,193 @@ +/* + 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 . +*/ + +#include "common.h" +#include "record.h" +#include "utils.h" +#include "debug.h" + +#define RING_SIZE_MS 500 + +static void CALLBACK in_proc(HWAVEIN handle, UINT msg, DWORD user_data, DWORD param1, + DWORD param2) +{ + WaveRecorder* recorder = (WaveRecorder*)user_data; + recorder->trigger(); +} + +WaveRecorder::WaveRecorder(Platform::RecordClinet& client, uint32_t sampels_per_sec, + uint32_t bits_per_sample, uint32_t channels) + : _client (client) + , _ring (NULL) + , _frame (NULL) + , _head (0) + , _in_use (0) +{ + WAVEFORMATEX info; + uint32_t frame_align; + + info.wFormatTag = WAVE_FORMAT_PCM; + info.nChannels = channels; + info.nSamplesPerSec = sampels_per_sec; + info.nBlockAlign = frame_align = channels * bits_per_sample / 8; + info.nAvgBytesPerSec = sampels_per_sec * info.nBlockAlign; + info.wBitsPerSample = bits_per_sample; + + + if (waveInOpen(&_wave_in, WAVE_MAPPER, &info, (DWORD_PTR)in_proc, (DWORD_PTR)this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + throw Exception("cannot open playback device"); + } + + try { + const int frame_size = WavePlaybackAbstract::FRAME_SIZE; + uint32_t frame_bytes = frame_size * channels * bits_per_sample / 8; + + _frame = new uint8_t[frame_bytes]; + _frame_pos = _frame; + _frame_end = _frame + frame_bytes; + init_ring(sampels_per_sec, frame_bytes, frame_align); + _client.add_evnet_sorce(*this); + } catch (...) { + delete[] _ring; + delete[] _frame; + waveInClose(_wave_in); + throw; + } +} + +WaveRecorder::~WaveRecorder() +{ + waveInReset(_wave_in); + reclaim(); + _client.remove_evnet_sorce(*this); + waveInClose(_wave_in); + delete[] _ring; + delete[] _frame; +} + +void WaveRecorder::init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align) +{ + const int frame_size = WavePlaybackAbstract::FRAME_SIZE; + + _ring_size = (sampels_per_sec * RING_SIZE_MS / 1000) / frame_size; + _ring_item_size = sizeof(WAVEHDR) + frame_bytes + frame_align; + + int ring_bytes = _ring_size * _ring_item_size; + _ring = new uint8_t[ring_bytes]; + + uint8_t* ptr = _ring; + uint8_t* end = ptr + ring_bytes; + for (; ptr != end; ptr += _ring_item_size) { + WAVEHDR* buf = (WAVEHDR*)ptr; + memset(ptr, 0, _ring_item_size); + buf->dwBufferLength = frame_bytes; + ULONG_PTR ptr = (ULONG_PTR)(buf + 1); + ptr = (ptr + frame_align - 1) / frame_align * frame_align; + buf->lpData = (LPSTR)(buf + 1); + } +} + +void WaveRecorder::start() +{ + _frame_pos = _frame; + waveInReset(_wave_in); + push_frames(); + waveInStart(_wave_in); +} + +inline WAVEHDR* WaveRecorder::wave_hader(uint32_t position) +{ + ASSERT(position < _ring_size); + return (WAVEHDR*)(_ring + position * _ring_item_size); +} + +inline void WaveRecorder::move_head() +{ + _head = (_head + 1) % _ring_size; + _in_use--; +} + +void WaveRecorder::push_frames() +{ + while (_in_use != _ring_size) { + WAVEHDR* buff = wave_hader((_head + _in_use) % _ring_size); + ++_in_use; + + MMRESULT err = waveInPrepareHeader(_wave_in, buff, sizeof(WAVEHDR)); + if (err != MMSYSERR_NOERROR) { + THROW("waveInPrepareHeader filed %d", err); + } + err = waveInAddBuffer(_wave_in, buff, sizeof(WAVEHDR)); + if (err != MMSYSERR_NOERROR) { + THROW("waveInAddBuffer filed %d", err); + } + } +} + +void WaveRecorder::on_event() +{ + while (_in_use) { + WAVEHDR* front_buf = wave_hader(_head); + if (!(front_buf->dwFlags & WHDR_DONE)) { + break; + } + waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR)); + front_buf->dwFlags &= ~WHDR_DONE; + int n = front_buf->dwBytesRecorded; + front_buf->dwBytesRecorded = 0; + uint8_t* ptr = (uint8_t*)front_buf->lpData; + while (n) { + int now = MIN(n, _frame_end - _frame_pos); + memcpy(_frame_pos, ptr, now); + if ((_frame_pos += n) == _frame_end) { + _client.push_frame(_frame); + _frame_pos = _frame; + } + n -= now; + ptr += now; + } + move_head(); + push_frames(); + } +} + +void WaveRecorder::reclaim() +{ + while (_in_use) { + WAVEHDR* front_buf = wave_hader(_head); + if (!(front_buf->dwFlags & WHDR_DONE)) { + break; + } + waveInUnprepareHeader(_wave_in, front_buf, sizeof(WAVEHDR)); + front_buf->dwFlags &= ~WHDR_DONE; + front_buf->dwBytesRecorded = 0; + move_head(); + } +} + +void WaveRecorder::stop() +{ + waveInReset(_wave_in); + reclaim(); +} + +bool WaveRecorder::abort() +{ + return true; +} + diff --git a/client/windows/record.h b/client/windows/record.h new file mode 100644 index 00000000..dab69fa0 --- /dev/null +++ b/client/windows/record.h @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#ifndef _H_WINDOWS_RECORD +#define _H_WINDOWS_RECORD + +#include "audio_devices.h" +#include "platform.h" + +class WaveRecorder: public WaveRecordAbstract, public EventsLoop::Trigger { +public: + WaveRecorder(Platform::RecordClinet& client, uint32_t sampels_per_sec, + uint32_t bits_per_sample, uint32_t channels); + virtual ~WaveRecorder(); + + virtual void start(); + virtual void stop(); + virtual bool abort(); + + virtual void on_event(); + +private: + void init_ring(uint32_t sampels_per_sec, uint32_t frame_bytes, uint32_t frame_align); + WAVEHDR* wave_hader(uint32_t position); + void move_head(); + + void reclaim(); + void push_frames(); + +private: + Platform::RecordClinet& _client; + HWAVEIN _wave_in; + uint8_t* _ring; + uint32_t _ring_item_size; + uint32_t _ring_size; + uint32_t _head; + uint32_t _in_use; + uint8_t* _frame; + uint8_t* _frame_pos; + uint8_t* _frame_end; +}; + +#endif + diff --git a/client/windows/red_drawable.cpp b/client/windows/red_drawable.cpp new file mode 100644 index 00000000..13d4a21e --- /dev/null +++ b/client/windows/red_drawable.cpp @@ -0,0 +1,151 @@ +/* + 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 . +*/ + +#include "common.h" +#include "red_drawable.h" +#include "pixels_source_p.h" +#include "utils.h" +#include "threads.h" + +static const uint64_t lock_timout = 1000 * 1000 * 10; /*10ms*/ + +void RedDrawable::copy_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest) +{ + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + PixelsSource_p* src_p_data = (PixelsSource_p*)src.get_opaque(); + + for (;;) { + Lock lock(*dest_p_data->_mutex); + Lock timed_lock(*src_p_data->_mutex, lock_timout); + if (!timed_lock.is_locked()) { + continue; + } + BitBlt(dest_p_data->dc, dest.left + _origin.x, dest.top + _origin.y, + dest.right - dest.left, dest.bottom - dest.top, + src_p_data->dc, src_x + src._origin.x, + src_y + src._origin.y, SRCCOPY); + return; + } +} + +void RedDrawable::blend_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest) +{ + static BLENDFUNCTION blend_func = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA}; + + int width = dest.right - dest.left; + int height = dest.bottom - dest.top; + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + PixelsSource_p* src_p_data = (PixelsSource_p*)src.get_opaque(); + for (;;) { + Lock lock(*dest_p_data->_mutex); + Lock timed_lock(*src_p_data->_mutex, lock_timout); + if (!timed_lock.is_locked()) { + continue; + } + AlphaBlend(dest_p_data->dc, dest.left + _origin.x, dest.top + _origin.y, width, height, + src_p_data->dc, src_x + src._origin.x, src_y + src._origin.y, width, height, + blend_func); + return; + } +} + +void RedDrawable::combine_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest, + CombineOP op) +{ + DWORD rop; + switch (op) { + case OP_COPY: + rop = SRCCOPY; + break; + case OP_AND: + rop = SRCAND; + break; + case OP_XOR: + rop = SRCINVERT; + break; + default: + THROW("invalid op %d", op); + } + + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + PixelsSource_p* src_p_data = (PixelsSource_p*)src.get_opaque(); + for (;;) { + Lock lock(*dest_p_data->_mutex); + Lock timed_lock(*src_p_data->_mutex, lock_timout); + if (!timed_lock.is_locked()) { + continue; + } + BitBlt(dest_p_data->dc, dest.left + _origin.x, dest.top + _origin.y, + dest.right - dest.left, dest.bottom - dest.top, + src_p_data->dc, src_x + src._origin.x, + src_y + src._origin.y, rop); + return; + } +} + +void RedDrawable::erase_rect(const Rect& rect, rgb32_t color) +{ + RECT r; + r.left = rect.left + _origin.x; + r.right = rect.right + _origin.x; + r.top = rect.top + _origin.y; + r.bottom = rect.bottom + _origin.y; + + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + Lock lock(*dest_p_data->_mutex); + FillRect(dest_p_data->dc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); +} + +void RedDrawable::fill_rect(const Rect& rect, rgb32_t color) +{ + RECT r; + r.left = rect.left + _origin.x; + r.right = rect.right + _origin.x; + r.top = rect.top + _origin.y; + r.bottom = rect.bottom + _origin.y; + + HBRUSH brush = CreateSolidBrush(RGB(rgb32_get_red(color), + rgb32_get_green(color), + rgb32_get_blue(color))); + for (;;) { + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + Lock lock(*dest_p_data->_mutex); + FillRect(dest_p_data->dc, &r, brush); + break; + } + DeleteObject(brush); +} + +void RedDrawable::frame_rect(const Rect& rect, rgb32_t color) +{ + RECT r; + r.left = rect.left + _origin.x; + r.right = rect.right + _origin.x; + r.top = rect.top + _origin.y; + r.bottom = rect.bottom + _origin.y; + HBRUSH brush = CreateSolidBrush(RGB(rgb32_get_red(color), + rgb32_get_green(color), + rgb32_get_blue(color))); + for (;;) { + PixelsSource_p* dest_p_data = (PixelsSource_p*)get_opaque(); + Lock lock(*dest_p_data->_mutex); + FrameRect(dest_p_data->dc, &r, brush); + break; + } + DeleteObject(brush); +} + diff --git a/client/windows/red_pixmap.cpp b/client/windows/red_pixmap.cpp new file mode 100644 index 00000000..bd9d7e41 --- /dev/null +++ b/client/windows/red_pixmap.cpp @@ -0,0 +1,42 @@ +/* + 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 . +*/ + +#include "common.h" +#include "red_pixmap.h" +#include "debug.h" +#include "utils.h" + +RedPixmap::RedPixmap(int width, int height, RedPixmap::Format format, + bool top_bottom, rgb32_t* pallet) + : _format (format) + , _width (width) + , _height (height) + , _stride (ALIGN(width * (_format == RedPixmap::A1 ? 1: 32), 32) / 8) + , _top_bottom (top_bottom) + , _data (NULL) +{ +} + +RedPixmap::~RedPixmap() +{ +} + +bool RedPixmap::is_big_endian_bits() +{ + return _format == RedPixmap::A1; +} + diff --git a/client/windows/red_pixmap_cairo.cpp b/client/windows/red_pixmap_cairo.cpp new file mode 100644 index 00000000..0b3073c4 --- /dev/null +++ b/client/windows/red_pixmap_cairo.cpp @@ -0,0 +1,101 @@ +/* + 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 . +*/ + +#include "common.h" +#include "red_pixmap_cairo.h" +#include "red_pixmap.h" +#include "debug.h" +#include "utils.h" +#include "pixels_source_p.h" +#include "platform_utils.h" + +struct RedPixmap_p { + PixelsSource_p pixels_source_p; + HBITMAP prev_bitmap; +}; + +static inline int format_to_bpp(RedPixmap::Format format) +{ + return ((format == RedPixmap::A1) ? 1 : 32); +} + +RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format, bool top_bottom, + rgb32_t* pallet, RedWindow *win) + : RedPixmap(width, height, format, top_bottom, pallet) +{ + ASSERT(format == RedPixmap::ARGB32 || format == RedPixmap::RGB32 || format == RedPixmap::A1); + ASSERT(sizeof(RedPixmap_p) <= PIXELES_SOURCE_OPAQUE_SIZE); + + struct { + BITMAPINFO inf; + RGBQUAD palette[255]; + } bitmap_info; + + memset(&bitmap_info, 0, sizeof(bitmap_info)); + bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader); + bitmap_info.inf.bmiHeader.biWidth = _width; + bitmap_info.inf.bmiHeader.biHeight = top_bottom ? -_height : _height; + +/*#ifdef USE_OGL + // ----------------------------------------------------------------------------- + // ensure valid access to additional stride. + // apparently glReadPixels validate ((ptr of last line) + GL_PACK_ROW_LENGTH + 1). + // seen on "ATI Radeon HD 2400 PRO" "2.0.6479 Release" + if (top_bottom) { + bitmap_info.inf.bmiHeader.biHeight--; + } else { + bitmap_info.inf.bmiHeader.biHeight++; + } + //------------------------------------------------------------------------------ +#endif*/ + + bitmap_info.inf.bmiHeader.biPlanes = 1; + bitmap_info.inf.bmiHeader.biBitCount = format_to_bpp(format); + bitmap_info.inf.bmiHeader.biCompression = BI_RGB; + switch (format) { + case RedPixmap::A1: + for (int i = 0; i < (1 << format_to_bpp(format)); i++) { + bitmap_info.inf.bmiColors[i].rgbRed = rgb32_get_red(pallet[i]); + bitmap_info.inf.bmiColors[i].rgbGreen = rgb32_get_green(pallet[i]); + bitmap_info.inf.bmiColors[i].rgbBlue = rgb32_get_blue(pallet[i]); + } + break; + } + AutoDC dc(create_compatible_dc()); + AutoGDIObject bitmap(CreateDIBSection(dc.get(), &bitmap_info.inf, 0, + (VOID **)&_data, NULL, 0)); + if (!bitmap.valid()) { + THROW("create compatible bitmap failed"); + } +/*#ifdef USE_OGL + SetWindowOrgEx(dc.get(), 0, -1, NULL); // compensate for one pad line +#endif*/ + ((RedPixmap_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(), bitmap.release()); + ((RedPixmap_p*)get_opaque())->pixels_source_p.dc = dc.release(); +} + +RedPixmapCairo::~RedPixmapCairo() +{ + HDC dc = ((RedPixmap_p*)get_opaque())->pixels_source_p.dc; + if (dc) { + HBITMAP prev_bitmap = ((RedPixmap_p*)get_opaque())->prev_bitmap; + HBITMAP bitmap = (HBITMAP)SelectObject(dc, prev_bitmap); + DeleteObject(bitmap); + DeleteDC(dc); + } +} + diff --git a/client/windows/red_pixmap_gdi.cpp b/client/windows/red_pixmap_gdi.cpp new file mode 100644 index 00000000..adb21da8 --- /dev/null +++ b/client/windows/red_pixmap_gdi.cpp @@ -0,0 +1,102 @@ +/* + 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 . +*/ + +#include "common.h" +#include "red_pixmap_gdi.h" +#include "red_pixmap.h" +#include "debug.h" +#include "utils.h" +#include "pixels_source_p.h" +#include "platform_utils.h" + +struct RedPixmap_p { + PixelsSource_p pixels_source_p; + HBITMAP prev_bitmap; +}; + +static inline int format_to_bpp(RedPixmap::Format format) +{ + return ((format == RedPixmap::A1) ? 1 : 32); +} + +RedPixmapGdi::RedPixmapGdi(int width, int height, RedPixmap::Format format, bool top_bottom, + rgb32_t* pallet) + : RedPixmap(width, height, format, top_bottom, pallet) +{ + ASSERT(format == RedPixmap::ARGB32 || format == RedPixmap::RGB32 || format == RedPixmap::A1); + ASSERT(sizeof(RedPixmap_p) <= PIXELES_SOURCE_OPAQUE_SIZE); + + struct { + BITMAPINFO inf; + RGBQUAD palette[255]; + } bitmap_info; + + memset(&bitmap_info, 0, sizeof(bitmap_info)); + bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader); + bitmap_info.inf.bmiHeader.biWidth = _width; + bitmap_info.inf.bmiHeader.biHeight = top_bottom ? -_height : _height; + + bitmap_info.inf.bmiHeader.biPlanes = 1; + bitmap_info.inf.bmiHeader.biBitCount = format_to_bpp(format); + bitmap_info.inf.bmiHeader.biCompression = BI_RGB; + switch (format) { + case RedPixmap::A1: + for (int i = 0; i < (1 << format_to_bpp(format)); i++) { + bitmap_info.inf.bmiColors[i].rgbRed = rgb32_get_red(pallet[i]); + bitmap_info.inf.bmiColors[i].rgbGreen = rgb32_get_green(pallet[i]); + bitmap_info.inf.bmiColors[i].rgbBlue = rgb32_get_blue(pallet[i]); + } + break; + } + AutoDC dc(create_compatible_dc()); + AutoGDIObject bitmap(CreateDIBSection(dc.get(), &bitmap_info.inf, 0, + (VOID **)&_data, NULL, 0)); + if (!bitmap.valid()) { + THROW("create compatible bitmap failed"); + } + memset(_data, 1, 1); + ((RedPixmap_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(), bitmap.release()); + ((RedPixmap_p*)get_opaque())->pixels_source_p.dc = dc.release(); +} + +HDC RedPixmapGdi::get_dc() +{ + return ((RedPixmap_p*)get_opaque())->pixels_source_p.dc; +} + +void *RedPixmapGdi::get_memptr() +{ + return _data; +} + +RedPixmapGdi::~RedPixmapGdi() +{ + HDC dc = ((RedPixmap_p*)get_opaque())->pixels_source_p.dc; + if (dc) { + HBITMAP prev_bitmap = ((RedPixmap_p*)get_opaque())->prev_bitmap; + HBITMAP bitmap = (HBITMAP)SelectObject(dc, prev_bitmap); + DeleteObject(bitmap); + DeleteDC(dc); + } +} + +Mutex& RedPixmapGdi::get_mutex() +{ + RedPixmap_p* p_data = (RedPixmap_p*)get_opaque(); + return *p_data->pixels_source_p._mutex; +} + diff --git a/client/windows/red_window.cpp b/client/windows/red_window.cpp new file mode 100644 index 00000000..0b25a798 --- /dev/null +++ b/client/windows/red_window.cpp @@ -0,0 +1,933 @@ +/* + 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 . +*/ + +#include "common.h" +#include "red_window.h" +#include "pixels_source_p.h" +#include "utils.h" +#include "debug.h" +#include "red.h" +#include "menu.h" +#include "win_platform.h" +#include "platform_utils.h" + +#define NATIVE_CAPTION_STYLE (WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) + +extern HINSTANCE instance; + +static ATOM class_atom = 0; +static const LPCWSTR win_class_name = L"redc_wclass"; +static HWND focus_window = NULL; +static HHOOK low_keyboard_hook = NULL; + + +static inline int to_red_mouse_state(WPARAM wParam) +{ + return ((wParam & MK_LBUTTON) ? REDC_LBUTTON_MASK : 0) | + ((wParam & MK_MBUTTON) ? REDC_MBUTTON_MASK : 0) | + ((wParam & MK_RBUTTON) ? REDC_RBUTTON_MASK : 0); +} + +static inline RedKey translate_key(int virtual_key, uint32_t scan, bool escape) +{ + if (scan == 0) { + return REDKEY_INVALID; + } + switch (virtual_key) { + case VK_PAUSE: + return REDKEY_PAUSE; + case VK_SNAPSHOT: + return REDKEY_CTRL_PRINT_SCREEN; + case VK_NUMLOCK: + return REDKEY_NUM_LOCK; + case VK_HANGUL: + return REDKEY_KOREAN_HANGUL; + case VK_HANJA: + return REDKEY_KOREAN_HANGUL_HANJA; + case VK_PROCESSKEY: + if (scan == 0xf1) { + return REDKEY_INVALID; // prevent double key (VK_PROCESSKEY + VK_HANJA) + } else if (scan == 0xf2) { + return REDKEY_KOREAN_HANGUL; + } + default: + //todo: always use vitrtual key + if (escape) { + scan += REDKEY_ESCAPE_BASE; + } + return (RedKey)scan; + } +} + +static int menu_cmd_to_app(WPARAM wparam) +{ + return 0; +} + +LRESULT CALLBACK RedWindow_p::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + RedWindow* window = (RedWindow*)GetWindowLong(hWnd, GWL_USERDATA); + ASSERT(window); + + switch (message) { + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc; + + hdc = BeginPaint(hWnd, &ps); + Point origin = window->get_origin(); + Rect r; + r.left = ps.rcPaint.left - origin.x; + r.top = ps.rcPaint.top - origin.y; + r.right = ps.rcPaint.right - origin.x; + r.bottom = ps.rcPaint.bottom - origin.y; + window->get_listener().on_exposed_rect(r); + EndPaint(hWnd, &ps); + break; + } + case WM_MOUSEMOVE: { + if (!window->_pointer_in_window) { + window->on_pointer_enter(); + } + Point origin = window->get_origin(); + window->get_listener().on_mouse_motion(LOWORD(lParam) - origin.x, HIWORD(lParam) - origin.y, + to_red_mouse_state(wParam)); + break; + } + case WM_MOUSELEAVE: + window->on_pointer_leave(); + break; + case WM_SETFOCUS: + window->on_focus_in(); + break; + case WM_KILLFOCUS: + window->on_focus_out(); + break; + case WM_LBUTTONDOWN: + window->get_listener().on_button_press(REDC_MOUSE_LBUTTON, to_red_mouse_state(wParam)); + break; + case WM_LBUTTONUP: + window->get_listener().on_button_release(REDC_MOUSE_LBUTTON, to_red_mouse_state(wParam)); + break; + case WM_RBUTTONDOWN: + window->get_listener().on_button_press(REDC_MOUSE_RBUTTON, to_red_mouse_state(wParam)); + break; + case WM_RBUTTONUP: + window->get_listener().on_button_release(REDC_MOUSE_RBUTTON, to_red_mouse_state(wParam)); + break; + case WM_MBUTTONDOWN: + window->get_listener().on_button_press(REDC_MOUSE_MBUTTON, to_red_mouse_state(wParam)); + break; + case WM_MBUTTONUP: + window->get_listener().on_button_release(REDC_MOUSE_MBUTTON, to_red_mouse_state(wParam)); + break; + case WM_MOUSEWHEEL: + if (HIWORD(wParam) & 0x8000) { + window->get_listener().on_button_press(REDC_MOUSE_DBUTTON, + to_red_mouse_state(wParam)); + window->get_listener().on_button_release(REDC_MOUSE_DBUTTON, + to_red_mouse_state(wParam)); + } else { + window->get_listener().on_button_press(REDC_MOUSE_UBUTTON, + to_red_mouse_state(wParam)); + window->get_listener().on_button_release(REDC_MOUSE_UBUTTON, + to_red_mouse_state(wParam)); + } + break; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: { + RedKey key = translate_key(wParam, HIWORD(lParam) & 0xff, (lParam & (1 << 24)) != 0); + window->get_listener().on_key_press(key); + // Allow Windows to translate Alt-F4 to WM_CLOSE message. + if (!window->_key_interception) { + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + } + case WM_SYSKEYUP: + case WM_KEYUP: { + RedKey key = translate_key(wParam, HIWORD(lParam) & 0xff, (lParam & (1 << 24)) != 0); + window->get_listener().on_key_release(key); + break; + } + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) { + window->get_listener().on_deactivate(); + } else { + window->get_listener().on_activate(); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_GETMINMAXINFO: { + MINMAXINFO* info = (MINMAXINFO*)lParam; + info->ptMaxSize.x = window->_window_size.x; + info->ptMaxSize.y = window->_window_size.y; + info->ptMinTrackSize = info->ptMaxSize; + info->ptMaxTrackSize = info->ptMaxSize; + info->ptMaxPosition.x = info->ptMaxPosition.y = 0; + break; + } + case WM_SYSCOMMAND: + if (window->prossec_menu_commands(wParam & ~0x0f)) { + break; + } + return DefWindowProc(hWnd, message, wParam, lParam); + case WM_ENTERSIZEMOVE: + case WM_ENTERMENULOOP: + window->get_listener().enter_modal_loop(); + return DefWindowProc(hWnd, message, wParam, lParam); + case WM_EXITSIZEMOVE: + case WM_EXITMENULOOP: + window->get_listener().exit_modal_loop(); + return DefWindowProc(hWnd, message, wParam, lParam); + case WM_SETCURSOR: + if (!window->_pointer_in_window) { + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + case WM_SIZE: + if (wParam == SIZE_MINIMIZED) { + window->on_minimized(); + } else if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) { + window->on_restored(); + } + break; + case WM_WINDOWPOSCHANGING: + window->on_pos_changing(*window); + return DefWindowProc(hWnd, message, wParam, lParam); + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +static ATOM register_class(HINSTANCE instance) +{ + WNDCLASSEX wclass; + + wclass.cbSize = sizeof(WNDCLASSEX); + wclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wclass.lpfnWndProc = DefWindowProc; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + wclass.hInstance = instance; + wclass.hIcon = NULL; + wclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wclass.hbrBackground = NULL; + wclass.lpszMenuName = NULL; + wclass.lpszClassName = win_class_name; + wclass.hIconSm = NULL; + return RegisterClassEx(&wclass); +} + +RedWindow_p::RedWindow_p() + : _win (NULL) + , _modal_refs (0) + , _no_taskmgr_dll (NULL) + , _no_taskmgr_hook (NULL) + , _minimized (false) + , _valid_pos (false) + , _sys_menu (NULL) +{ +} + +void RedWindow_p::create(RedWindow& red_window, PixelsSource_p& pixels_source) +{ + HWND window; + if (!(window = CreateWindow(win_class_name, L"", NATIVE_CAPTION_STYLE, CW_USEDEFAULT, + 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL))) { + THROW("create window failed"); + } + HDC dc = GetDC(window); + if (!dc) { + THROW("get dc failed"); + } + _win = window; + pixels_source.dc = dc; + SetWindowLong(window, GWL_USERDATA, (LONG)&red_window); + SetWindowLong(window, GWL_WNDPROC, (LONG)WindowProc); +} + +void RedWindow_p::destroy(PixelsSource_p& pixels_source) +{ + if (!_win) { + return; + } + + ReleaseDC(_win, pixels_source.dc); + SetWindowLong(_win, GWL_WNDPROC, (LONG)DefWindowProc); + SetWindowLong(_win, GWL_USERDATA, NULL); + DestroyWindow(_win); +} + +void RedWindow_p::on_pos_changing(RedWindow& red_window) +{ + if (_minimized || IsIconic(_win)) { + return; + } + Point pos = red_window.get_position(); + _x = pos.x; + _y = pos.y; + _valid_pos = true; +} + +void RedWindow_p::on_minimized() +{ + _minimized = true; +} + +void RedWindow_p::on_restored() +{ + if (!_minimized) { + return; + } + _minimized = false; + if (!_valid_pos) { + return; + } + _valid_pos = false; + SetWindowPos(_win, NULL, _x, _y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); +} + +bool RedWindow_p::prossec_menu_commands(int cmd) +{ + CommandMap::iterator iter = _commands_map.find(cmd); + if (iter == _commands_map.end()) { + return false; + } + (*iter).second.menu->get_target().do_command((*iter).second.command); + return true; +} + +RedWindow::RedWindow(RedWindow::Listener& listener, int screen_id) + : _listener (listener) + , _type (TYPE_NORMAL) + , _local_cursor (NULL) + , _cursor_visible (true) + , _focused (false) + , _pointer_in_window (false) + , _key_interception (false) + , _menu (NULL) +{ + RECT win_rect; + + create(*this, *(PixelsSource_p*)get_opaque()); + GetWindowRect(_win, &win_rect); + _window_size.x = win_rect.right - win_rect.left; + _window_size.y = win_rect.bottom - win_rect.top; +} + +RedWindow::~RedWindow() +{ + release_menu(_menu); + destroy(*(PixelsSource_p*)get_opaque()); + if (_local_cursor) { + _local_cursor->unref(); + } +} + +void RedWindow::set_title(std::wstring& title) +{ + SetWindowText(_win, title.c_str()); +} + +void RedWindow::set_icon(Icon* icon) +{ + if (!icon) { + return; + } + WinIcon* w_icon = (WinIcon *)icon; + SendMessage(_win, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)w_icon->get_handle()); + SendMessage(_win, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)w_icon->get_handle()); +} + +void RedWindow::raise() +{ + SetWindowPos(_win, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); +} + +void RedWindow::position_after(RedWindow *win) +{ + HWND after = NULL; + + if (win) { + after = win->_win; + } + SetWindowPos(_win, after, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); +} + +static LONG to_native_style(RedWindow::Type type) +{ + LONG win_style; + + switch (type) { + case RedWindow::TYPE_NORMAL: + win_style = NATIVE_CAPTION_STYLE; + break; + case RedWindow::TYPE_FULLSCREEN: + win_style = 0; + break; + default: + THROW("invalid type %d", type); + } + return win_style; +} + +void RedWindow::show(int screen_id) +{ + if (IsIconic(_win)) { + ShowWindow(_win, SW_RESTORE); + } + + const UINT set_pos_flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_FRAMECHANGED; + HWND pos; + + SetWindowLong(_win, GWL_STYLE, to_native_style(_type)); + switch (_type) { + case TYPE_NORMAL: + pos = HWND_NOTOPMOST; + break; + case TYPE_FULLSCREEN: + pos = HWND_TOPMOST; + break; + default: + THROW("invalid type %d", _type); + } + SetWindowPos(_win, pos, 0, 0, 0, 0, set_pos_flags); +} + +void RedWindow::external_show() +{ + LONG_PTR style = ::GetWindowLongPtr(_win, GWL_STYLE); + if ((style & WS_MINIMIZE) == WS_MINIMIZE) { + ShowWindow(_win, SW_RESTORE); + } else { + // Handle the case when hide() was called and the window is not + // visible. Since we're not the active window, the call just set the + // windows' style and doesn't show the window. + if ((style & WS_VISIBLE) != WS_VISIBLE) { + show(0); + } + // We're not the active the window, so we must be attached to the + // calling thread's message queue before focus is grabbed. + HWND front = GetForegroundWindow(); + if (front != NULL) { + DWORD thread = GetWindowThreadProcessId(front, NULL); + AttachThreadInput(thread, GetCurrentThreadId(), TRUE); + SetFocus(_win); + AttachThreadInput(thread, GetCurrentThreadId(), FALSE); + } + } +} + +void RedWindow::hide() +{ + ShowWindow(_win, SW_HIDE); +} + +static void client_to_window_size(HWND win, int width, int height, Point& win_size, + RedWindow::Type type) +{ + RECT area; + + SetRect(&area, 0, 0, width, height); + AdjustWindowRectEx(&area, to_native_style(type), FALSE, GetWindowLong(win, GWL_EXSTYLE)); + win_size.x = area.right - area.left; + win_size.y = area.bottom - area.top; +} + +void RedWindow::move_and_resize(int x, int y, int width, int height) +{ + client_to_window_size(_win, width, height, _window_size, _type); + SetWindowPos(_win, NULL, x, y, _window_size.x, _window_size.y, SWP_NOACTIVATE | SWP_NOZORDER); + if (_minimized) { + _valid_pos = true; + _x = x; + _y = y; + } +} + +void RedWindow::move(int x, int y) +{ + SetWindowPos(_win, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); + if (_minimized) { + _valid_pos = true; + _x = x; + _y = y; + } +} + +void RedWindow::resize(int width, int height) +{ + client_to_window_size(_win, width, height, _window_size, _type); + SetWindowPos(_win, NULL, 0, 0, _window_size.x, _window_size.y, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); +} + +void RedWindow::activate() +{ + SetActiveWindow(_win); +} + +void RedWindow::minimize() +{ + ShowWindow(_win, SW_SHOWMINIMIZED); +} + +void RedWindow::set_mouse_position(int x, int y) +{ + POINT pt; + pt.x = x + get_origin().x; + pt.y = y + get_origin().y; + ClientToScreen(_win, &pt); + SetCursorPos(pt.x, pt.y); +} + +class Region_p { +public: + Region_p(HRGN region) : _region (region) {} + ~Region_p() {} + + void get_bbox(Rect& bbox) const + { + RECT box; + + if (GetRgnBox(_region, &box) == 0) { + THROW("get region bbox failed"); + } + bbox.left = box.left; + bbox.right = box.right; + bbox.top = box.top; + bbox.bottom = box.bottom; + } + + bool contains_point(int x, int y) const + { + return !!PtInRegion(_region, x, y); + } + +private: + HRGN _region; +}; + +bool RedWindow::get_mouse_anchor_point(Point& pt) +{ + AutoGDIObject region(CreateRectRgn(0, 0, 0, 0)); + WindowDC win_dc(_win); + + GetRandomRgn(*win_dc, (HRGN)region.get(), SYSRGN); + Point anchor; + Region_p region_p((HRGN)region.get()); + if (!find_anchor_point(region_p, anchor)) { + return false; + } + POINT screen_pt; + screen_pt.x = anchor.x; + screen_pt.y = anchor.y; + ScreenToClient(_win, &screen_pt); + pt.x = screen_pt.x - get_origin().x; + pt.y = screen_pt.y - get_origin().y; + return true; +} + +void RedWindow::cupture_mouse() +{ + RECT client_rect; + POINT origin; + + origin.x = origin.y = 0; + ClientToScreen(_win, &origin); + GetClientRect(_win, &client_rect); + OffsetRect(&client_rect, origin.x, origin.y); + ClipCursor(&client_rect); +} + +void RedWindow::release_mouse() +{ + ClipCursor(NULL); +} + +void RedWindow::set_cursor(LocalCursor* local_cursor) +{ + ASSERT(local_cursor); + if (_local_cursor) { + _local_cursor->unref(); + } + _local_cursor = local_cursor->ref(); + if (_pointer_in_window) { + _local_cursor->set(_win); + while (ShowCursor(TRUE) < 0); + } + _cursor_visible = true; +} + +void RedWindow::hide_cursor() +{ + if (_cursor_visible) { + if (_pointer_in_window) { + while (ShowCursor(FALSE) > -1); + } + _cursor_visible = false; + } +} + +void RedWindow::show_cursor() +{ + if (!_cursor_visible) { + if (_pointer_in_window) { + while (ShowCursor(TRUE) < 0); + } + _cursor_visible = true; + } +} + +Point RedWindow::get_position() +{ + Point position; + if (_minimized || IsIconic(_win)) { + if (_valid_pos) { + position.x = _x; + position.y = _y; + } else { + position.x = position.y = 0; + } + } else { + RECT window_rect; + GetWindowRect(_win, &window_rect); + position.x = window_rect.left; + position.y = window_rect.top; + } + return position; +} + +Point RedWindow::get_size() +{ + RECT client_rect; + GetClientRect(_win, &client_rect); + Point pt = {client_rect.right - client_rect.left, client_rect.bottom - client_rect.top}; + return pt; +} + +static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if ((nCode == HC_ACTION)) { + KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT*)lParam; + DWORD dwMsg = 1; + + // dwMsg shall contain the information that would be stored + // in the usual lParam argument of a WM_KEYDOWN message. + // All information like hardware scan code and other flags + // are stored within one double word at different bit offsets. + // Refer to MSDN for further information: + // + // (Keystroke Messages) + dwMsg += hooked->scanCode << 16; + dwMsg += hooked->flags << 24; + + // Forward these keys so the keyboard leds will light up. + BOOL bNextHook = ((hooked->vkCode == VK_CAPITAL) || + (hooked->vkCode == VK_SCROLL) || (hooked->vkCode == VK_NUMLOCK)); + + // In some cases scan code of VK_RSHIFT is fake shift (probably a bug) so we + // convert it to non extended code. Also, QEmu doesn't expect num-lock to be + // an extended key. + if ((hooked->vkCode == VK_NUMLOCK) || (hooked->vkCode == VK_RSHIFT)) { + dwMsg &= ~(1 << 24); + } + + SendMessage(focus_window, wParam, hooked->vkCode, dwMsg); + + if (bNextHook == FALSE) { + return 1; + } + } + + // In all other cases, we call the next hook and return it's value. + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +void RedWindow::do_start_key_interception() +{ + if (low_keyboard_hook) { + return; + } + low_keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, + GetModuleHandle(NULL), 0); + _listener.on_start_key_interception(); +} + +void RedWindow::do_stop_key_interception() +{ + if (!low_keyboard_hook) { + return; + } + + UnhookWindowsHookEx(low_keyboard_hook); + low_keyboard_hook = NULL; + _listener.on_stop_key_interception(); +} + +void RedWindow::start_key_interception() +{ + if (_key_interception) { + return; + } + _key_interception = true; + focus_window = _win; + if (_focused && _pointer_in_window) { + do_start_key_interception(); + } +} + +void RedWindow::stop_key_interception() +{ + if (!_key_interception) { + return; + } + _key_interception = false; + if (_focused && _pointer_in_window) { + do_stop_key_interception(); + } +} + +void RedWindow::init() +{ + if (!(class_atom = register_class(instance))) { + THROW("register class failed"); + } +} + +#ifdef USE_OGL + +void RedWindow::touch_context_draw() +{ +} + +void RedWindow::touch_context_copy() +{ +} + +void RedWindow::untouch_context() +{ +} + +#endif + +void RedWindow::set_type_gl() +{ +} + +void RedWindow::unset_type_gl() +{ +} + +void RedWindow::on_focus_in() +{ + _focused = true; + if (_pointer_in_window && _key_interception) { + do_start_key_interception(); + } +} + +void RedWindow::on_focus_out() +{ + if (!_focused) { + return; + } + _focused = false; + do_stop_key_interception(); +} + +void RedWindow::on_pointer_enter() +{ + if (_pointer_in_window) { + return; + } + if (_cursor_visible) { + if (_local_cursor) { + _local_cursor->set(_win); + } + while (ShowCursor(TRUE) < 0); + } else { + while (ShowCursor(FALSE) > -1); + } + _pointer_in_window = true; + _listener.on_pointer_enter(); + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = _win; + if (!TrackMouseEvent(&tme)) { + THROW("track mouse event failed"); + } + if (_focused && _key_interception) { + do_start_key_interception(); + } +} + +void RedWindow::on_pointer_leave() +{ + if (!_pointer_in_window) { + return; + } + if (!_cursor_visible) { + while (ShowCursor(TRUE) < 0); + } + _pointer_in_window = false; + _listener.on_pointer_leave(); + if (_focused && _key_interception) { + do_stop_key_interception(); + } +} + +static void insert_seperator(HMENU menu) +{ + MENUITEMINFO item_info; + item_info.cbSize = sizeof(item_info); + item_info.fMask = MIIM_TYPE; + item_info.fType = MFT_SEPARATOR; + item_info.dwTypeData = NULL; + item_info.dwItemData = 0; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &item_info); +} + +static void utf8_to_wchar(const std::string& src, std::wstring& dest) +{ + int len = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, NULL, 0); + if (!len) { + THROW("fail to conver utf8 to wchar"); + } + dest.resize(len); + MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, (wchar_t *)dest.c_str(), len); +} + +static void insert_command(HMENU menu, const std::string& name, int id) +{ + MENUITEMINFO item_info; + item_info.cbSize = sizeof(item_info); + item_info.fMask = MIIM_TYPE | MIIM_ID; + item_info.fType = MFT_STRING; + std::wstring wname; + utf8_to_wchar(name, wname); + item_info.cch = wname.size(); + item_info.dwTypeData = (wchar_t *)wname.c_str(); + item_info.wID = id; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &item_info); +} + +static HMENU insert_sub_menu(HMENU menu, const std::string& name) +{ + MENUITEMINFO item_info; + item_info.cbSize = sizeof(item_info); + item_info.fMask = MIIM_TYPE | MIIM_SUBMENU; + item_info.fType = MFT_STRING; + std::wstring wname; + utf8_to_wchar(name, wname); + item_info.cch = wname.size(); + item_info.dwTypeData = (wchar_t *)wname.c_str(); + item_info.hSubMenu = CreateMenu(); + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &item_info); + return item_info.hSubMenu; +} + +static int next_free_id = 1; +static const int last_id = 0x0f00; + +static std::list free_sys_menu_id; + +static int alloc_sys_cmd_id() +{ + if (!free_sys_menu_id.empty()) { + int ret = *free_sys_menu_id.begin(); + free_sys_menu_id.pop_front(); + return ret; + } + if (next_free_id == last_id) { + THROW("failed"); + } + + return next_free_id++ << 4; +} + +static void free_sys_cmd_id(int id) +{ + free_sys_menu_id.push_back(id >> 4); +} + +static void insert_menu(Menu* menu, HMENU native, CommandMap& _commands_map) +{ + int pos = 0; + + for (;; pos++) { + Menu::ItemType type = menu->item_type_at(pos); + switch (type) { + case Menu::MENU_ITEM_TYPE_COMMAND: { + std::string name; + int command_id; + menu->command_at(pos, name, command_id); + int sys_command = alloc_sys_cmd_id(); + _commands_map[sys_command] = CommandInfo(menu, command_id); + insert_command(native, name, sys_command); + break; + } + case Menu::MENU_ITEM_TYPE_MENU: { + AutoRef sub_menu(menu->sub_at(pos)); + HMENU native_sub = insert_sub_menu(native, (*sub_menu)->get_name()); + insert_menu(*sub_menu, native_sub, _commands_map); + break; + } + case Menu::MENU_ITEM_TYPE_SEPARATOR: + insert_seperator(native); + break; + case Menu::MENU_ITEM_TYPE_INVALID: + return; + } + } +} + +void RedWindow_p::release_menu(Menu* menu) +{ + if (menu) { + while (!_commands_map.empty()) { + free_sys_cmd_id((*_commands_map.begin()).first); + _commands_map.erase(_commands_map.begin()); + } + GetSystemMenu(_win, TRUE); + _sys_menu = NULL; + menu->unref(); + return; + } +} + +void RedWindow::set_menu(Menu* menu) +{ + release_menu(_menu); + _menu = NULL; + + if (!menu) { + return; + } + _menu = menu->ref(); + _sys_menu = GetSystemMenu(_win, FALSE); + insert_seperator(_sys_menu); + insert_menu(_menu, _sys_menu, _commands_map); +} + diff --git a/client/windows/red_window_p.h b/client/windows/red_window_p.h new file mode 100644 index 00000000..4b5655ec --- /dev/null +++ b/client/windows/red_window_p.h @@ -0,0 +1,68 @@ +/* + 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 . +*/ + +#ifndef _H_RED_WINDOW_P +#define _H_RED_WINDOW_P + +#include + +class RedWindow; +class Menu; +struct PixelsSource_p; + +typedef HWND Window; + +class CommandInfo { +public: + CommandInfo() : menu (0), command (NULL) {} + CommandInfo(Menu* in_menu, int in_command) : menu (in_menu), command (in_command) {} + + Menu* menu; + int command; +}; + +typedef std::map CommandMap; + +class RedWindow_p { +public: + RedWindow_p(); + + void create(RedWindow& red_window, PixelsSource_p& pixels_source); + void destroy(PixelsSource_p& pixels_source); + void release_menu(Menu* menu); + void on_minimized(); + void on_restored(); + void on_pos_changing(RedWindow& red_window); + bool prossec_menu_commands(int cmd); + + static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +protected: + HWND _win; + uint32_t _modal_refs; + HMODULE _no_taskmgr_dll; + HHOOK _no_taskmgr_hook; + bool _minimized; + bool _valid_pos; + int _x; + int _y; + CommandMap _commands_map; + HMENU _sys_menu; +}; + +#endif + diff --git a/client/windows/redc.rc b/client/windows/redc.rc new file mode 100644 index 00000000..9524d1bd --- /dev/null +++ b/client/windows/redc.rc @@ -0,0 +1,138 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +SPLASH_IMAGE_RES_ID BITMAP "splash.bmp" +INFO_IMAGE_RES_ID BITMAP "static_title.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,3,99,0 + PRODUCTVERSION 0,3,99,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "CompanyName", "Red Hat Technologies INC" + VALUE "FileDescription", "Spice client" + VALUE "FileVersion", "0, 3, 99, 0" + VALUE "InternalName", "spicec" + VALUE "LegalCopyright", "Copyright (c) 2009 Red Hat, Inc. and/or its affiliates.INC. All rights reserved." + VALUE "OriginalFilename", "spicec.exe" + VALUE "ProductName", "Red Hat Spice" + VALUE "ProductVersion", "0, 3, 99, 0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +RED_ICON_RES_ID ICON "solideice.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Neutral (Sys. Default) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT +#pragma code_page(1255) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "spicec.exe.manifest" +#endif // Neutral (Sys. Default) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/client/windows/redc.sln b/client/windows/redc.sln new file mode 100644 index 00000000..0562b95e --- /dev/null +++ b/client/windows/redc.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "redc", "redc.vcproj", "{4F03BAF9-DFBC-4CA7-B860-8929555981AE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4F03BAF9-DFBC-4CA7-B860-8929555981AE}.Debug|Win32.ActiveCfg = Debug|Win32 + {4F03BAF9-DFBC-4CA7-B860-8929555981AE}.Debug|Win32.Build.0 = Debug|Win32 + {4F03BAF9-DFBC-4CA7-B860-8929555981AE}.Release|Win32.ActiveCfg = Release|Win32 + {4F03BAF9-DFBC-4CA7-B860-8929555981AE}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/client/windows/redc.vcproj b/client/windows/redc.vcproj new file mode 100644 index 00000000..25ff3259 --- /dev/null +++ b/client/windows/redc.vcproj @@ -0,0 +1,597 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/windows/resource.h b/client/windows/resource.h new file mode 100644 index 00000000..51ff7f28 --- /dev/null +++ b/client/windows/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by redc.rc +// +#define RED_ICON_RES_ID 5 +#define SPLASH_IMAGE_RES_ID 101 +#define INFO_IMAGE_RES_ID 102 +#define IDI_ICON_APPLICATION 103 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/client/windows/solideice.ico b/client/windows/solideice.ico new file mode 100644 index 00000000..9187fb79 Binary files /dev/null and b/client/windows/solideice.ico differ diff --git a/client/windows/spicec.exe.manifest b/client/windows/spicec.exe.manifest new file mode 100644 index 00000000..b7d1d0a4 --- /dev/null +++ b/client/windows/spicec.exe.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/client/windows/splash.bmp b/client/windows/splash.bmp new file mode 100644 index 00000000..b1fba2fe Binary files /dev/null and b/client/windows/splash.bmp differ diff --git a/client/windows/static_title.bmp b/client/windows/static_title.bmp new file mode 100644 index 00000000..da575ae2 Binary files /dev/null and b/client/windows/static_title.bmp differ diff --git a/client/windows/stdint.h b/client/windows/stdint.h new file mode 100644 index 00000000..8b94f37e --- /dev/null +++ b/client/windows/stdint.h @@ -0,0 +1,394 @@ +/* ISO C9x 7.18 Integer types + + * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794) + + * + + * THIS SOFTWARE IS NOT COPYRIGHTED + + * + + * Contributor: Danny Smith + + * + + * This source code is offered for use in the public domain. You may + + * use, modify or distribute it freely. + + * + + * This code is distributed in the hope that it will be useful but + + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + + * DISCLAIMED. This includes but is not limited to warranties of + + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + * + + * Date: 2000-12-02 + + */ + + + + + +#ifndef _STDINT_H + +#define _STDINT_H + +#define __need_wint_t + +#define __need_wchar_t + +#include + + + +#ifdef _WIN32_WCE + +typedef _int64 int64_t; + +typedef unsigned _int64 uint64_t; + +#else + +typedef long long int64_t; + +typedef unsigned long long uint64_t; + +#endif /* _WIN32_WCE */ + + + +/* 7.18.1.1 Exact-width integer types */ + +typedef signed char int8_t; + +typedef unsigned char uint8_t; + +typedef short int16_t; + +typedef unsigned short uint16_t; + +typedef int int32_t; + +typedef unsigned uint32_t; + + + +/* 7.18.1.2 Minimum-width integer types */ + +typedef signed char int_least8_t; + +typedef unsigned char uint_least8_t; + +typedef short int_least16_t; + +typedef unsigned short uint_least16_t; + +typedef int int_least32_t; + +typedef unsigned uint_least32_t; + +#ifndef _WIN32_WCE + +typedef long long int_least64_t; + +typedef unsigned long long uint_least64_t; + +#endif + + + +/* 7.18.1.3 Fastest minimum-width integer types + + * Not actually guaranteed to be fastest for all purposes + + * Here we use the exact-width types for 8 and 16-bit ints. + + */ + +typedef char int_fast8_t; + +typedef unsigned char uint_fast8_t; + +typedef short int_fast16_t; + +typedef unsigned short uint_fast16_t; + +typedef int int_fast32_t; + +typedef unsigned int uint_fast32_t; + +#ifndef _WIN32_WCE + +typedef long long int_fast64_t; + +typedef unsigned long long uint_fast64_t; + +#endif + + + +/* 7.18.1.4 Integer types capable of holding object pointers */ + +typedef int intptr_t; + +typedef unsigned uintptr_t; + + + +/* 7.18.1.5 Greatest-width integer types */ + +#ifndef _WIN32_WCE + +typedef long long intmax_t; + +typedef unsigned long long uintmax_t; + +#endif + + + +/* 7.18.2 Limits of specified-width integer types */ + +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + + + +/* 7.18.2.1 Limits of exact-width integer types */ + +#define INT8_MIN (-128) + +#define INT16_MIN (-32768) + +#define INT32_MIN (-2147483647 - 1) + +#define INT64_MIN (-9223372036854775807LL - 1) + + + +#define INT8_MAX 127 + +#define INT16_MAX 32767 + +#define INT32_MAX 2147483647 + +#define INT64_MAX 9223372036854775807LL + + + +#define UINT8_MAX 0xff /* 255U */ + +#define UINT16_MAX 0xffff /* 65535U */ + +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */ + + + +/* 7.18.2.2 Limits of minimum-width integer types */ + +#define INT_LEAST8_MIN INT8_MIN + +#define INT_LEAST16_MIN INT16_MIN + +#define INT_LEAST32_MIN INT32_MIN + +#define INT_LEAST64_MIN INT64_MIN + + + +#define INT_LEAST8_MAX INT8_MAX + +#define INT_LEAST16_MAX INT16_MAX + +#define INT_LEAST32_MAX INT32_MAX + +#define INT_LEAST64_MAX INT64_MAX + + + +#define UINT_LEAST8_MAX UINT8_MAX + +#define UINT_LEAST16_MAX UINT16_MAX + +#define UINT_LEAST32_MAX UINT32_MAX + +#define UINT_LEAST64_MAX UINT64_MAX + + + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ + +#define INT_FAST8_MIN INT8_MIN + +#define INT_FAST16_MIN INT16_MIN + +#define INT_FAST32_MIN INT32_MIN + +#define INT_FAST64_MIN INT64_MIN + + + +#define INT_FAST8_MAX INT8_MAX + +#define INT_FAST16_MAX INT16_MAX + +#define INT_FAST32_MAX INT32_MAX + +#define INT_FAST64_MAX INT64_MAX + + + +#define UINT_FAST8_MAX UINT8_MAX + +#define UINT_FAST16_MAX UINT16_MAX + +#define UINT_FAST32_MAX UINT32_MAX + +#define UINT_FAST64_MAX UINT64_MAX + + + +/* 7.18.2.4 Limits of integer types capable of holding + + object pointers */ + +#define INTPTR_MIN INT32_MIN + +#define INTPTR_MAX INT32_MAX + +#define UINTPTR_MAX UINT32_MAX + + + +/* 7.18.2.5 Limits of greatest-width integer types */ + +#define INTMAX_MIN INT64_MIN + +#define INTMAX_MAX INT64_MAX + +#define UINTMAX_MAX UINT64_MAX + + + +/* 7.18.3 Limits of other integer types */ + +#define PTRDIFF_MIN INT32_MIN + +#define PTRDIFF_MAX INT32_MAX + + + +#define SIG_ATOMIC_MIN INT32_MIN + +#define SIG_ATOMIC_MAX INT32_MAX + + + +#define SIZE_MAX UINT32_MAX + + + +#ifndef WCHAR_MIN /* also in wchar.h */ + +#define WCHAR_MIN 0 + +#define WCHAR_MAX 0xffff /* UINT16_MAX */ + +#endif + + + +/* + + * wint_t is unsigned short for compatibility with MS runtime + + */ + +#define WINT_MIN 0 + +#define WINT_MAX 0xffff /* UINT16_MAX */ + + + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + + + + + +/* 7.18.4 Macros for integer constants */ + +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + + + +/* 7.18.4.1 Macros for minimum-width integer constants + + + + Accoding to Douglas Gwyn : + + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + + 9899:1999 as initially published, the expansion was required + + to be an integer constant of precisely matching type, which + + is impossible to accomplish for the shorter types on most + + platforms, because C99 provides no standard way to designate + + an integer constant with width less than that of type int. + + TC1 changed this to require just an integer constant + + *expression* with *promoted* type." + +*/ + + + +#define INT8_C(val) ((int8_t) + (val)) + +#define UINT8_C(val) ((uint8_t) + (val##U)) + +#define INT16_C(val) ((int16_t) + (val)) + +#define UINT16_C(val) ((uint16_t) + (val##U)) + + + +#define INT32_C(val) val##L + +#define UINT32_C(val) val##UL + +#define INT64_C(val) val##LL + +#define UINT64_C(val) val##ULL + + + +/* 7.18.4.2 Macros for greatest-width integer constants */ + +#define INTMAX_C(val) INT64_C(val) + +#define UINTMAX_C(val) UINT64_C(val) + + + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + + + +#endif + + + diff --git a/client/windows/win_platform.h b/client/windows/win_platform.h new file mode 100644 index 00000000..ddd0cd59 --- /dev/null +++ b/client/windows/win_platform.h @@ -0,0 +1,50 @@ +/* + 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 . +*/ + +#ifndef _H_WINPLATFORM +#define _H_WINPLATFORM + +#include "icon.h" + +class EventOwner { +public: + EventOwner() : _event_handle (0) {} + HANDLE const get_event_handle() { return _event_handle;} + virtual void on_event() = 0; + +protected: + HANDLE _event_handle; +}; + +class WinPlatform { +public: + static void add_event(EventOwner& event_owner); + static void remove_event(EventOwner& event_owner); +}; + +class WinIcon: public Icon { +public: + WinIcon(HICON icon) : _icon (icon) {} + + HICON get_handle() {return _icon;} + +private: + HICON _icon; +}; + +#endif + -- cgit