summaryrefslogtreecommitdiffstats
path: root/client/windows
diff options
context:
space:
mode:
authorYaniv Kamay <ykamay@redhat.com>2009-09-19 21:25:46 +0300
committerYaniv Kamay <ykamay@redhat.com>2009-10-14 15:06:41 +0200
commitc1b79eb035fa158fb2ac3bc8e559809611070016 (patch)
tree3348dd749a700dedf87c9b16fe8be77c62928df8 /client/windows
downloadspice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.gz
spice-c1b79eb035fa158fb2ac3bc8e559809611070016.tar.xz
spice-c1b79eb035fa158fb2ac3bc8e559809611070016.zip
fresh start
Diffstat (limited to 'client/windows')
-rw-r--r--client/windows/atomic_count.h42
-rw-r--r--client/windows/events_loop_p.cpp158
-rw-r--r--client/windows/events_loop_p.h42
-rw-r--r--client/windows/main.cpp124
-rw-r--r--client/windows/my_getopt.cpp20
-rw-r--r--client/windows/named_pipe.cpp227
-rw-r--r--client/windows/named_pipe.h94
-rw-r--r--client/windows/pixels_source.cpp108
-rw-r--r--client/windows/pixels_source_p.h29
-rw-r--r--client/windows/platform.cpp786
-rw-r--r--client/windows/platform_utils.cpp151
-rw-r--r--client/windows/platform_utils.h84
-rw-r--r--client/windows/playback.cpp177
-rw-r--r--client/windows/playback.h55
-rw-r--r--client/windows/record.cpp193
-rw-r--r--client/windows/record.h58
-rw-r--r--client/windows/red_drawable.cpp151
-rw-r--r--client/windows/red_pixmap.cpp42
-rw-r--r--client/windows/red_pixmap_cairo.cpp101
-rw-r--r--client/windows/red_pixmap_gdi.cpp102
-rw-r--r--client/windows/red_window.cpp933
-rw-r--r--client/windows/red_window_p.h68
-rw-r--r--client/windows/redc.rc138
-rw-r--r--client/windows/redc.sln20
-rw-r--r--client/windows/redc.vcproj597
-rw-r--r--client/windows/resource.h19
-rw-r--r--client/windows/solideice.icobin0 -> 15342 bytes
-rw-r--r--client/windows/spicec.exe.manifest10
-rw-r--r--client/windows/splash.bmpbin0 -> 921656 bytes
-rw-r--r--client/windows/static_title.bmpbin0 -> 29756 bytes
-rw-r--r--client/windows/stdint.h394
-rw-r--r--client/windows/win_platform.h50
32 files changed, 4973 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_EVENTS_LOOP_P
+#define _H_EVENTS_LOOP_P
+
+#include "common.h"
+
+#include <vector>
+
+class EventSource;
+
+class EventsLoop_p {
+public:
+ std::vector<EventSource*> _events;
+ std::vector<HANDLE> _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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include <fstream>
+#include <windows.h>
+extern "C" {
+#include "pthread.h"
+}
+
+//#define OPEN_CONSOLE
+#ifdef OPEN_CONSOLE
+#include <io.h>
+#include <conio.h>
+#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<uint8_t> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_NAMED_PIPE
+#define _H_NAMED_PIPE
+
+#include <windows.h>
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+
+#include "platform.h"
+#include "win_platform.h"
+#include "utils.h"
+#include "threads.h"
+#include "debug.h"
+#include "monitor.h"
+#include "record.h"
+#include "playback.h"
+#include "cursor.h"
+#include "named_pipe.h"
+
+#define WM_USER_WAKEUP WM_USER
+#define NUM_TIMERS 100
+
+int gdi_handlers = 0;
+extern HINSTANCE instance;
+
+class DefaultEventListener: public Platform::EventListener {
+public:
+ virtual void on_app_activated() {}
+ virtual void on_app_deactivated() {}
+ virtual void on_monitors_change() {}
+};
+
+static DefaultEventListener default_event_listener;
+static Platform::EventListener* event_listener = &default_event_listener;
+static HWND paltform_win;
+static HANDLE main_tread;
+
+struct Timer {
+ TimerID id;
+ timer_proc_t proc;
+ void* opaque;
+ Timer *next;
+};
+
+Timer timers[NUM_TIMERS];
+Timer* free_timers = NULL;
+Mutex timers_lock;
+
+static void free_timer(Timer* timer)
+{
+ Lock lock(timers_lock);
+ timer->proc = NULL;
+ timer->next = free_timers;
+ free_timers = timer;
+}
+
+static void init_timers()
+{
+ for (int i = 0; i < NUM_TIMERS; i++) {
+ timers[i].id = i;
+ free_timer(&timers[i]);
+ }
+}
+
+static Timer* alloc_timer()
+{
+ Timer* timer;
+
+ Lock lock(timers_lock);
+ if (!(timer = free_timers)) {
+ return NULL;
+ }
+
+ free_timers = free_timers->next;
+ return timer;
+}
+
+static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_TIMER: {
+ TimerID id = wParam - 1;
+ ASSERT(id < NUM_TIMERS);
+ Timer* timer = &timers[id];
+ timer->proc(timer->opaque, id);
+ break;
+ }
+ case WM_USER_WAKEUP: {
+ break;
+ }
+ case WM_ACTIVATEAPP:
+ if (wParam) {
+ event_listener->on_app_activated();
+ } else {
+ event_listener->on_app_deactivated();
+ }
+ break;
+ case WM_DISPLAYCHANGE:
+ event_listener->on_monitors_change();
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+static void create_message_wind()
+{
+ WNDCLASSEX wclass;
+ ATOM class_atom;
+ HWND window;
+
+ const LPCWSTR class_name = L"spicec_platform_wclass";
+
+ wclass.cbSize = sizeof(WNDCLASSEX);
+ wclass.style = 0;
+ wclass.lpfnWndProc = PlatformWinProc;
+ wclass.cbClsExtra = 0;
+ wclass.cbWndExtra = 0;
+ wclass.hInstance = instance;
+ wclass.hIcon = NULL;
+ wclass.hCursor = NULL;
+ wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wclass.lpszMenuName = NULL;
+ wclass.lpszClassName = class_name;
+ wclass.hIconSm = NULL;
+
+ if ((class_atom = RegisterClassEx(&wclass)) == 0) {
+ THROW("register class failed");
+ }
+
+ if (!(window = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) {
+ THROW("create message window failed");
+ }
+
+ paltform_win = window;
+}
+
+void Platform::send_quit_request()
+{
+ ASSERT(GetCurrentThread() == main_tread);
+ PostQuitMessage(0);
+}
+
+static std::vector<HANDLE> events;
+static std::vector<EventOwner*> events_owners;
+
+void WinPlatform::add_event(EventOwner& event_owner)
+{
+ ASSERT(main_tread == GetCurrentThread());
+ int size = events.size();
+ if (size == MAXIMUM_WAIT_OBJECTS - 1) {
+ THROW("reached maximum allowed events to wait for");
+ }
+ events.resize(size + 1);
+ events_owners.resize(size + 1);
+ events[size] = event_owner.get_event_handle();
+ events_owners[size] = &event_owner;
+}
+
+void WinPlatform::remove_event(EventOwner& event_owner)
+{
+ ASSERT(main_tread == GetCurrentThread());
+ int size = events.size();
+ for (int i = 0; i < size; i++) {
+ if (events_owners[i] == &event_owner) {
+ for (i++; i < size; i++) {
+ events[i - 1] = events[i];
+ events_owners[i - 1] = events_owners[i];
+ }
+ events.resize(size - 1);
+ events_owners.resize(size - 1);
+ return;
+ }
+ }
+ THROW("event owner not found");
+}
+
+void Platform::wait_events()
+{
+ if (!events.size()) {
+ if (!WaitMessage()) {
+ THROW("wait failed %d", GetLastError());
+ }
+ return;
+ }
+
+ DWORD r = MsgWaitForMultipleObjectsEx(events.size(), &events[0], INFINITE, QS_ALLINPUT, 0);
+ if (r == WAIT_OBJECT_0 + events.size()) {
+ return;
+ }
+ if (r >= WAIT_OBJECT_0 && r <= WAIT_OBJECT_0 + events.size() - 1) {
+ events_owners[r - WAIT_OBJECT_0]->on_event();
+ } else if (r == WAIT_FAILED) {
+ THROW("wait multiple failed %d", GetLastError());
+ } else {
+ THROW("unexpected wait return %u", r);
+ }
+}
+
+NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface)
+{
+ return (ListenerRef)(new WinListener(name, listener_interface));
+}
+
+void NamedPipe::destroy(ListenerRef listener_ref)
+{
+ delete (WinListener *)listener_ref;
+}
+
+void NamedPipe::destroy_connection(ConnectionRef conn_ref)
+{
+ delete (WinConnection *)conn_ref;
+}
+
+int32_t NamedPipe::read(ConnectionRef conn_ref, uint8_t *buf, int32_t size)
+{
+ return ((WinConnection *)conn_ref)->read(buf, size);
+}
+
+int32_t NamedPipe::write(ConnectionRef conn_ref, const uint8_t *buf, int32_t size)
+{
+ return ((WinConnection *)conn_ref)->write(buf, size);
+}
+
+void Platform::wakeup()
+{
+ if (!PostMessage(paltform_win, WM_USER_WAKEUP, 0, 0)) {
+ THROW("post failed %d", GetLastError());
+ }
+}
+
+bool Platform::process_events()
+{
+ MSG msg;
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT) {
+ return true;
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return false;
+}
+
+void Platform::msleep(unsigned int msec)
+{
+ Sleep(msec);
+}
+
+void Platform::yield()
+{
+ Sleep(0);
+}
+
+void Platform::set_thread_priority(void* thread, Platform::ThreadPriority in_priority)
+{
+ ASSERT(thread == NULL);
+ int priority;
+
+ switch (in_priority) {
+ case PRIORITY_TIME_CRITICAL:
+ priority = THREAD_PRIORITY_TIME_CRITICAL;
+ break;
+ case PRIORITY_HIGH:
+ priority = THREAD_PRIORITY_HIGHEST;
+ break;
+ case PRIORITY_ABOVE_NORMAL:
+ priority = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case PRIORITY_NORMAL:
+ priority = THREAD_PRIORITY_NORMAL;
+ break;
+ case PRIORITY_BELOW_NORMAL:
+ priority = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case PRIORITY_LOW:
+ priority = THREAD_PRIORITY_LOWEST;
+ break;
+ case PRIORITY_IDLE:
+ priority = THREAD_PRIORITY_IDLE;
+ break;
+ default:
+ THROW("invalid priority %d", in_priority);
+ }
+ SetThreadPriority(GetCurrentThread(), priority);
+}
+
+void Platform::set_event_listener(EventListener* listener)
+{
+ event_listener = listener ? listener : &default_event_listener;
+}
+
+TimerID Platform::create_interval_timer(timer_proc_t proc, void* opaque)
+{
+ Timer* timer = alloc_timer();
+ if (!timer) {
+ return INVALID_TIMER;
+ }
+ timer->proc = proc;
+ timer->opaque = opaque;
+ return timer->id;
+}
+
+bool Platform::activate_interval_timer(TimerID timer, unsigned int millisec)
+{
+ if (timer >= NUM_TIMERS) {
+ return false;
+ }
+
+ if (!SetTimer(paltform_win, timer + 1, millisec, NULL)) {
+ return false;
+ }
+ return true;
+}
+
+bool Platform::deactivate_interval_timer(TimerID timer)
+{
+ if (timer >= NUM_TIMERS) {
+ return false;
+ }
+ KillTimer(paltform_win, timer + 1);
+ return true;
+}
+
+void Platform::destroy_interval_timer(TimerID timer)
+{
+ if (timer == INVALID_TIMER) {
+ return;
+ }
+ ASSERT(timer < NUM_TIMERS);
+ KillTimer(paltform_win, timer + 1);
+ free_timer(&timers[timer]);
+}
+
+uint64_t Platform::get_monolithic_time()
+{
+ return uint64_t(GetTickCount()) * 1000 * 1000;
+}
+
+void Platform::get_temp_dir(std::string& path)
+{
+ DWORD len = GetTempPathA(0, NULL);
+ if (len <= 0) {
+ throw Exception("get temp patch failed");
+ }
+ char* tmp_path = new char[len + 1];
+ GetTempPathA(len, tmp_path);
+ path = tmp_path;
+ delete[] tmp_path;
+}
+
+class WinMonitor: public Monitor {
+public:
+ WinMonitor(int id, const wchar_t* name, const wchar_t* string);
+
+ virtual void set_mode(int width, int height);
+ virtual void restore();
+ virtual int get_depth() { return _depth;}
+ virtual Point get_position();
+ virtual Point get_size() const { Point size = {_width, _height}; return size;}
+ virtual bool is_out_of_sync() { return _out_of_sync;}
+ virtual int get_screen_id() { return 0;}
+
+protected:
+ virtual ~WinMonitor();
+
+private:
+ void update_position();
+ bool change_display_settings(int width, int height, int depth);
+ bool best_display_setting(uint32_t width, uint32_t height, uint32_t depth);
+
+private:
+ std::wstring _dev_name;
+ std::wstring _dev_string;
+ bool _active;
+ Point _position;
+ int _width;
+ int _height;
+ int _depth;
+ bool _out_of_sync;
+};
+
+WinMonitor::WinMonitor(int id, const wchar_t* name, const wchar_t* string)
+ : Monitor(id)
+ , _dev_name (name)
+ , _dev_string (string)
+ , _active (false)
+ , _out_of_sync (false)
+{
+ update_position();
+}
+
+WinMonitor::~WinMonitor()
+{
+ restore();
+}
+
+void WinMonitor::update_position()
+{
+ DEVMODE mode;
+ mode.dmSize = sizeof(DEVMODE);
+ mode.dmDriverExtra = 0;
+ EnumDisplaySettings(_dev_name.c_str(), ENUM_CURRENT_SETTINGS, &mode);
+ _position.x = mode.dmPosition.x;
+ _position.y = mode.dmPosition.y;
+ _width = mode.dmPelsWidth;
+ _height = mode.dmPelsHeight;
+ _depth = mode.dmBitsPerPel;
+}
+
+Point WinMonitor::get_position()
+{
+ update_position();
+ return _position;
+}
+
+bool WinMonitor::change_display_settings(int width, int height, int depth)
+{
+ DEVMODE mode;
+ mode.dmSize = sizeof(DEVMODE);
+ mode.dmDriverExtra = 0;
+ mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
+ mode.dmPelsWidth = width;
+ mode.dmPelsHeight = height;
+ mode.dmBitsPerPel = depth;
+
+ return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL)
+ == DISP_CHANGE_SUCCESSFUL;
+}
+
+bool WinMonitor::best_display_setting(uint32_t width, uint32_t height, uint32_t depth)
+{
+ DEVMODE mode;
+ DWORD mode_id = 0;
+ uint32_t mod_waste = ~0;
+ DWORD mod_width;
+ DWORD mod_height;
+ DWORD mod_depth;
+ DWORD mod_frequency;
+
+ mode.dmSize = sizeof(DEVMODE);
+ mode.dmDriverExtra = 0;
+ while (EnumDisplaySettings(_dev_name.c_str(), mode_id++, &mode)) {
+ // Workaround for
+ // Lenovo T61p, Nvidia Quadro FX 570M and
+ // Lenovo T61, Nvidia Quadro NVS 140M
+ //
+ // with dual monitors configuration
+ //
+ // we get strange values from EnumDisplaySettings 640x480x4 frequency 1
+ // and calling ChangeDisplaySettingsEx with that configuration result with
+ // machine that is stucked for a long period of time
+ if (mode.dmDisplayFrequency == 1) {
+ continue;
+ }
+
+ if (mode.dmPelsWidth >= width && mode.dmPelsHeight >= height) {
+ bool replace = false;
+ uint32_t curr_waste = mode.dmPelsWidth * mode.dmPelsHeight - width * height;
+ if (curr_waste < mod_waste) {
+ replace = true;
+ } else if (curr_waste == mod_waste) {
+ if (mod_depth == mode.dmBitsPerPel) {
+ replace = mode.dmDisplayFrequency > mod_frequency;
+ } else if (mod_depth != depth && mode.dmBitsPerPel > mod_depth) {
+ replace = true;
+ }
+ }
+ if (replace) {
+ mod_waste = curr_waste;
+ mod_width = mode.dmPelsWidth;
+ mod_height = mode.dmPelsHeight;
+ mod_depth = mode.dmBitsPerPel;
+ mod_frequency = mode.dmDisplayFrequency;
+ }
+ }
+ }
+ if (mod_waste == ~0) {
+ return false;
+ }
+ mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
+ mode.dmPelsWidth = mod_width;
+ mode.dmPelsHeight = mod_height;
+ mode.dmBitsPerPel = mod_depth;
+ mode.dmDisplayFrequency = mod_frequency;
+
+ return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL)
+ == DISP_CHANGE_SUCCESSFUL;
+}
+
+void WinMonitor::set_mode(int width, int height)
+{
+ update_position();
+ if (width == _width && height == _height) {
+ _out_of_sync = false;
+ return;
+ }
+ self_monitors_change++;
+ if (!change_display_settings(width, height, 32) && !best_display_setting(width, height, 32)) {
+ _out_of_sync = true;
+ } else {
+ _out_of_sync = false;
+ }
+ self_monitors_change--;
+ _active = true;
+ update_position();
+}
+
+void WinMonitor::restore()
+{
+ if (_active) {
+ _active = false;
+ self_monitors_change++;
+ ChangeDisplaySettingsEx(_dev_name.c_str(), NULL, NULL, 0, NULL);
+ self_monitors_change--;
+ }
+}
+
+static MonitorsList monitors;
+
+const MonitorsList& Platform::init_monitors()
+{
+ ASSERT(monitors.empty());
+
+ int id = 0;
+ DISPLAY_DEVICE device_info;
+ DWORD device_id = 0;
+ device_info.cb = sizeof(device_info);
+ while (EnumDisplayDevices(NULL, device_id, &device_info, 0)) {
+ if ((device_info.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
+ !(device_info.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) {
+ monitors.push_back(new WinMonitor(id++, device_info.DeviceName,
+ device_info.DeviceString));
+ }
+ device_id++;
+ }
+ return monitors;
+}
+
+void Platform::destroy_monitors()
+{
+ while (!monitors.empty()) {
+ Monitor* monitor = monitors.front();
+ monitors.pop_front();
+ delete monitor;
+ }
+}
+
+bool Platform::is_monitors_pos_valid()
+{
+ return true;
+}
+
+void Platform::init()
+{
+ main_tread = GetCurrentThread();
+ create_message_wind();
+ init_timers();
+}
+
+WaveRecordAbstract* Platform::create_recorder(RecordClinet& client,
+ uint32_t sampels_per_sec,
+ uint32_t bits_per_sample,
+ uint32_t channels)
+{
+ return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels);
+}
+
+WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
+ uint32_t bits_per_sample,
+ uint32_t channels)
+{
+ return new WavePlayer(sampels_per_sec, bits_per_sample, channels);
+}
+
+static void toggle_modifier(int key)
+{
+ INPUT inputs[2];
+ memset(inputs, 0, sizeof(inputs));
+ inputs[0].type = inputs[1].type = INPUT_KEYBOARD;
+ inputs[0].ki.wVk = inputs[1].ki.wVk = key;
+ inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ SendInput(2, inputs, sizeof(INPUT));
+}
+
+uint32_t Platform::get_keyboard_modifiers()
+{
+ uint32_t modifiers = 0;
+ if ((GetKeyState(VK_SCROLL) & 1)) {
+ modifiers |= SCROLL_LOCK_MODIFIER;
+ }
+ if ((GetKeyState(VK_NUMLOCK) & 1)) {
+ modifiers |= NUM_LOCK_MODIFIER;
+ }
+ if ((GetKeyState(VK_CAPITAL) & 1)) {
+ modifiers |= CAPS_LOCK_MODIFIER;
+ }
+ return modifiers;
+}
+
+void Platform::set_keyboard_modifiers(uint32_t modifiers)
+{
+ if (((modifiers >> SCROLL_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_SCROLL) & 1)) {
+ toggle_modifier(VK_SCROLL);
+ }
+
+ if (((modifiers >> Platform::NUM_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_NUMLOCK) & 1)) {
+ toggle_modifier(VK_NUMLOCK);
+ }
+
+ if (((modifiers >> CAPS_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_CAPITAL) & 1)) {
+ toggle_modifier(VK_CAPITAL);
+ }
+}
+
+class WinBaseLocalCursor: public LocalCursor {
+public:
+ WinBaseLocalCursor() : _handle (0) {}
+ void set(Window window) { SetCursor(_handle);}
+
+protected:
+ HCURSOR _handle;
+};
+
+class WinLocalCursor: public WinBaseLocalCursor {
+public:
+ WinLocalCursor(CursorData* cursor_data);
+ ~WinLocalCursor();
+
+private:
+ bool _shared;
+};
+
+WinLocalCursor::WinLocalCursor(CursorData* cursor_data)
+ : _shared (false)
+{
+ const CursorHeader& header = cursor_data->header();
+ const uint8_t* data = cursor_data->data();
+ int cur_size;
+ int bits = get_size_bits(header, cur_size);
+ if (!bits) {
+ THROW("invalid curosr type");
+ }
+ if (header.type == CURSOR_TYPE_MONO) {
+ _handle = CreateCursor(NULL, header.hot_spot_x, header.hot_spot_y,
+ header.width, header.height, data, data + cur_size);
+ return;
+ }
+ ICONINFO icon;
+ icon.fIcon = FALSE;
+ icon.xHotspot = header.hot_spot_x;
+ icon.yHotspot = header.hot_spot_y;
+ icon.hbmColor = icon.hbmMask = NULL;
+ HDC hdc = GetDC(NULL);
+
+ switch (header.type) {
+ case CURSOR_TYPE_ALPHA:
+ case CURSOR_TYPE_COLOR32:
+ case CURSOR_TYPE_COLOR16: {
+ BITMAPV5HEADER bmp_hdr;
+ ZeroMemory(&bmp_hdr, sizeof(bmp_hdr));
+ bmp_hdr.bV5Size = sizeof(bmp_hdr);
+ bmp_hdr.bV5Width = header.width;
+ bmp_hdr.bV5Height = -header.height;
+ bmp_hdr.bV5Planes = 1;
+ bmp_hdr.bV5BitCount = bits;
+ bmp_hdr.bV5Compression = BI_BITFIELDS;
+ if (bits == 32) {
+ bmp_hdr.bV5RedMask = 0x00FF0000;
+ bmp_hdr.bV5GreenMask = 0x0000FF00;
+ bmp_hdr.bV5BlueMask = 0x000000FF;
+ } else if (bits == 16) {
+ bmp_hdr.bV5RedMask = 0x00007C00;
+ bmp_hdr.bV5GreenMask = 0x000003E0;
+ bmp_hdr.bV5BlueMask = 0x0000001F;
+ }
+ if (header.type == CURSOR_TYPE_ALPHA) {
+ bmp_hdr.bV5AlphaMask = 0xFF000000;
+ }
+ void* bmp_pixels = NULL;
+ icon.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *)&bmp_hdr, DIB_RGB_COLORS, &bmp_pixels,
+ NULL, 0);
+ memcpy(bmp_pixels, data, cur_size);
+ icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1,
+ (header.type == CURSOR_TYPE_ALPHA) ? NULL :
+ (CONST VOID *)(data + cur_size));
+ break;
+ }
+ case CURSOR_TYPE_COLOR4: {
+ BITMAPINFO* bmp_info;
+ bmp_info = (BITMAPINFO *)new uint8_t[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) << bits)];
+ ZeroMemory(bmp_info, sizeof(BITMAPINFO));
+ bmp_info->bmiHeader.biSize = sizeof(bmp_info->bmiHeader);
+ bmp_info->bmiHeader.biWidth = header.width;
+ bmp_info->bmiHeader.biHeight = -header.height;
+ bmp_info->bmiHeader.biPlanes = 1;
+ bmp_info->bmiHeader.biBitCount = bits;
+ bmp_info->bmiHeader.biCompression = BI_RGB;
+ memcpy(bmp_info->bmiColors, data + cur_size, sizeof(RGBQUAD) << bits);
+ icon.hbmColor = CreateDIBitmap(hdc, &bmp_info->bmiHeader, CBM_INIT, data,
+ bmp_info, DIB_RGB_COLORS);
+ icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1,
+ (CONST VOID *)(data + cur_size + (sizeof(uint32_t) << bits)));
+ delete[] (uint8_t *)bmp_info;
+ break;
+ }
+ case CURSOR_TYPE_COLOR24:
+ case CURSOR_TYPE_COLOR8:
+ default:
+ LOG_WARN("unsupported cursor type %d", header.type);
+ _handle = LoadCursor(NULL, IDC_ARROW);
+ _shared = true;
+ ReleaseDC(NULL, hdc);
+ return;
+ }
+
+ ReleaseDC(NULL, hdc);
+
+ if (icon.hbmColor && icon.hbmMask) {
+ _handle = CreateIconIndirect(&icon);
+ }
+ if (icon.hbmMask) {
+ DeleteObject(icon.hbmMask);
+ }
+ if (icon.hbmColor) {
+ DeleteObject(icon.hbmColor);
+ }
+}
+
+WinLocalCursor::~WinLocalCursor()
+{
+ if (_handle && !_shared) {
+ DestroyCursor(_handle);
+ }
+}
+
+LocalCursor* Platform::create_local_cursor(CursorData* cursor_data)
+{
+ return new WinLocalCursor(cursor_data);
+}
+
+class WinInactiveCursor: public WinBaseLocalCursor {
+public:
+ WinInactiveCursor() { _handle = LoadCursor(NULL, IDC_NO);}
+};
+
+LocalCursor* Platform::create_inactive_cursor()
+{
+ return new WinInactiveCursor();
+}
+
+class WinDefaultCursor: public WinBaseLocalCursor {
+public:
+ WinDefaultCursor() { _handle = LoadCursor(NULL, IDC_ARROW);}
+};
+
+LocalCursor* Platform::create_default_cursor()
+{
+ return new WinDefaultCursor();
+}
+
+void Platform::set_display_mode_listner(DisplayModeListner* listener)
+{
+}
+
+Icon* Platform::load_icon(int id)
+{
+ HICON icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(id));
+ if (!icon) {
+ return NULL;
+ }
+ return new WinIcon(icon);
+}
+
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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include <map>
+#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<char> 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<wchar_t> 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<uint8_t> 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<int, const char*> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_PLATFORM_UTILS
+#define _H_PLATFORM_UTILS
+
+#define mb() __asm {lock add [esp], 0}
+
+template<class T, class FreeRes = FreeObject<T>, 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<HDC, Delete_DC> AutoDC;
+
+class Delete_Object {
+public:
+ void operator () (HGDIOBJ obj) { DeleteObject(obj);}
+};
+typedef AutoRes<HGDIOBJ, Delete_Object> AutoGDIObject;
+
+class DeleteOGLContext {
+public:
+ void operator () (HGLRC ctx) { wglDeleteContext(ctx);}
+};
+
+typedef AutoRes<HGLRC, DeleteOGLContext> 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<HDC, Delete_DC> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<int> 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<Menu> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _H_RED_WINDOW_P
+#define _H_RED_WINDOW_P
+
+#include <map>
+
+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<int, CommandInfo> 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 @@
+<?xml version="1.0" encoding="windows-1255"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="redc"
+ ProjectGUID="{4F03BAF9-DFBC-4CA7-B860-8929555981AE}"
+ RootNamespace="redc"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(REDC_BUILD_DIR)$(ConfigurationName)"
+ IntermediateDirectory="$(REDC_BUILD_DIR)$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".;..;&quot;..\..\common\win\my_getopt-1.5&quot;;..\..\common;&quot;$(SPICE_LIBS)\include&quot;;&quot;$(SPICE_LIBS)\include\qcairo&quot;;&quot;$(SPICE_LIBS)\include\ffmpeg&quot;"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;CAIRO_CANVAS_CACH_IS_SHARED;RED_DEBUG;CAIRO_CANVAS_NO_CHUNKS;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="1"
+ PrecompiledHeaderThrough="common.h"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="log4cppD.lib libqcairo-2.dll.lib libeay32MTd.lib ssleay32MTd.lib ws2_32.lib msimg32.lib winmm.lib avcodec-52.lib avutil-49.lib libcelt_0_5_1D.lib pthreadsD.lib version.lib"
+ OutputFile="$(OutDir)\spicec.exe"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
+ IgnoreAllDefaultLibraries="false"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(REDC_BUILD_DIR)$(ConfigurationName)"
+ IntermediateDirectory="$(REDC_BUILD_DIR)$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=".;..;&quot;..\..\common\win\my_getopt-1.5&quot;;..\..\common;&quot;$(SPICE_LIBS)\include&quot;;&quot;$(SPICE_LIBS)\include\qcairo&quot;;&quot;$(SPICE_LIBS)\include\ffmpeg&quot;"
+ PreprocessorDefinitions="WIN32;_WINDOWS;CAIRO_CANVAS_ACCESS_TEST;CAIRO_CANVAS_CACHE;CAIRO_CANVAS_NO_CHUNKS;CAIRO_CANVAS_CACH_IS_SHARED;_WIN32_WINNT=0x0500;LOG4CPLUS_STATIC;USE_GLZ;PTW32_STATIC_LIB"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="log4cpp.lib libqcairo-2.dll.lib libeay32MT.lib ssleay32MT.lib ws2_32.lib msimg32.lib winmm.lib avcodec-52.lib avutil-49.lib libcelt_0_5_1.lib pthreads.lib version.lib"
+ OutputFile="$(OutDir)\spicec.exe"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="&quot;$(SPICE_LIBS)\lib&quot;"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\application.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\cairo_canvas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\canvas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\canvas_utils.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\cmd_line_parser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\cursor.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\cursor_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\display_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\events_loop_p.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\gdi_canvas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoder.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoder_window.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\hot_keys.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\inputs_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\lz.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\menu.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\monitor.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\my_getopt.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ ObjectFile="$(IntDir)\$(InputName)1.obj"
+ XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\named_pipe.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\pixels_source.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\platform.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\platform_utils.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\playback.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\playback_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\quic.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\record.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\record_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\red_cairo_canvas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\red_channel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\red_client.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\red_drawable.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\red_gdi_canvas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\red_peer.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\red_pixmap.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\red_pixmap_cairo.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\red_pixmap_gdi.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\red_window.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\region.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\rop3.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\screen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\screen_layer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\threads.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\utils.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\application.h"
+ >
+ </File>
+ <File
+ RelativePath=".\atomic_count.h"
+ >
+ </File>
+ <File
+ RelativePath="..\cache.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\common.h"
+ >
+ </File>
+ <File
+ RelativePath="..\cursor.h"
+ >
+ </File>
+ <File
+ RelativePath="..\cursor_channel.h"
+ >
+ </File>
+ <File
+ RelativePath="..\debug.h"
+ >
+ </File>
+ <File
+ RelativePath="..\display_channel.h"
+ >
+ </File>
+ <File
+ RelativePath="..\events_loop.h"
+ >
+ </File>
+ <File
+ RelativePath=".\events_loop_p.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\gdi_canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\win\my_getopt-1.5\getopt.h"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoded_image.h"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoder_config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\glz_decoder_window.h"
+ >
+ </File>
+ <File
+ RelativePath="..\hot_keys.h"
+ >
+ </File>
+ <File
+ RelativePath="..\icon.h"
+ >
+ </File>
+ <File
+ RelativePath="..\inputs_channel.h"
+ >
+ </File>
+ <File
+ RelativePath="..\inputs_handler.h"
+ >
+ </File>
+ <File
+ RelativePath="..\menu.h"
+ >
+ </File>
+ <File
+ RelativePath="..\monitor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe.h"
+ >
+ </File>
+ <File
+ RelativePath=".\pixels_source_p.h"
+ >
+ </File>
+ <File
+ RelativePath="..\platform.h"
+ >
+ </File>
+ <File
+ RelativePath=".\platform_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\playback.h"
+ >
+ </File>
+ <File
+ RelativePath=".\record.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_cairo_canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_channel.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_client.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_drawable.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_gdi_canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_key.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_peer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\red_window.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath="..\screen.h"
+ >
+ </File>
+ <File
+ RelativePath="..\screen_layer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_cache.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\threads.h"
+ >
+ </File>
+ <File
+ RelativePath="..\utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win_platform.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\redc.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\solideice.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\spicec.exe.manifest"
+ >
+ </File>
+ <File
+ RelativePath=".\splash.bmp"
+ >
+ </File>
+ <File
+ RelativePath=".\static_title.bmp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
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
--- /dev/null
+++ b/client/windows/solideice.ico
Binary files 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 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/client/windows/splash.bmp b/client/windows/splash.bmp
new file mode 100644
index 00000000..b1fba2fe
--- /dev/null
+++ b/client/windows/splash.bmp
Binary files differ
diff --git a/client/windows/static_title.bmp b/client/windows/static_title.bmp
new file mode 100644
index 00000000..da575ae2
--- /dev/null
+++ b/client/windows/static_title.bmp
Binary files 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 <stdint.h>
+
+ * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794)
+
+ *
+
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+
+ *
+
+ * Contributor: Danny Smith <danny_r_smith_2001@yahoo.co.nz>
+
+ *
+
+ * 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 <stddef.h>
+
+
+
+#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 <gwyn@arl.mil>:
+
+ "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 <http://www.gnu.org/licenses/>.
+*/
+
+#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
+