source: vendor/synergy/current/lib/arch/CArchConsoleWindows.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 11.2 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CArchConsoleWindows.h"
16#include "IArchMultithread.h"
17#include "CArch.h"
18#include "CArchMiscWindows.h"
19#include <richedit.h>
20
21#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021
22#define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022
23#define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023
24#define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024
25#define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025
26
27//
28// CArchConsoleWindows
29//
30
31CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL;
32HINSTANCE CArchConsoleWindows::s_appInstance = NULL;
33
34CArchConsoleWindows::CArchConsoleWindows(void* appInstance) :
35 m_show(false),
36 m_maxLines(1000),
37 m_numCharacters(0),
38 m_maxCharacters(65536)
39{
40 // save the singleton instance
41 s_instance = this;
42
43 // save app instance
44 s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
45
46 // we need a mutex
47 m_mutex = ARCH->newMutex();
48
49 // and a condition variable which uses the above mutex
50 m_ready = false;
51 m_condVar = ARCH->newCondVar();
52
53 // we're going to want to get a result from the thread we're
54 // about to create to know if it initialized successfully.
55 // so we lock the condition variable.
56 ARCH->lockMutex(m_mutex);
57
58 // open a window and run an event loop in a separate thread.
59 // this has to happen in a separate thread because if we
60 // create a window on the current desktop with the current
61 // thread then the current thread won't be able to switch
62 // desktops if it needs to.
63 m_thread = ARCH->newThread(&CArchConsoleWindows::threadEntry, this);
64
65 // wait for child thread
66 while (!m_ready) {
67 ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
68 }
69
70 // ready
71 ARCH->unlockMutex(m_mutex);
72
73}
74
75CArchConsoleWindows::~CArchConsoleWindows()
76{
77 if (m_thread != NULL) {
78 PostMessage(m_hwnd, WM_QUIT, 0, 0);
79 ARCH->wait(m_thread, -1.0);
80 ARCH->closeThread(m_thread);
81 }
82 ARCH->closeCondVar(m_condVar);
83 ARCH->closeMutex(m_mutex);
84 s_instance = NULL;
85}
86
87void
88CArchConsoleWindows::openConsole(const char* title)
89{
90 SetWindowText(m_frame, title);
91 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0);
92}
93
94void
95CArchConsoleWindows::closeConsole()
96{
97 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0);
98 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0);
99}
100
101void
102CArchConsoleWindows::showConsole(bool showIfEmpty)
103{
104 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0);
105}
106
107void
108CArchConsoleWindows::writeConsole(const char* str)
109{
110 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE,
111 reinterpret_cast<WPARAM>(str), 0);
112}
113
114const char*
115CArchConsoleWindows::getNewlineForConsole()
116{
117 return "\r\n";
118}
119
120void
121CArchConsoleWindows::clearBuffer()
122{
123 m_buffer.clear();
124 m_numCharacters = 0;
125 SetWindowText(m_hwnd, "");
126}
127
128void
129CArchConsoleWindows::appendBuffer(const char* msg)
130{
131 bool wasEmpty = m_buffer.empty();
132
133 // get current selection
134 CHARRANGE selection;
135 SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection));
136
137 // remove tail of buffer
138 size_t removedCharacters = 0;
139 while (m_buffer.size() >= m_maxLines) {
140 removedCharacters += m_buffer.front().size();
141 m_buffer.pop_front();
142 }
143
144 // remove lines from top of control
145 if (removedCharacters > 0) {
146 CHARRANGE range;
147 range.cpMin = 0;
148 range.cpMax = static_cast<LONG>(removedCharacters);
149 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
150 SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast<LPARAM>(""));
151
152 // adjust selection
153 if (selection.cpMin < static_cast<LONG>(removedCharacters) ||
154 selection.cpMax < static_cast<LONG>(removedCharacters)) {
155 selection.cpMin = 0;
156 selection.cpMax = 0;
157 }
158 else {
159 selection.cpMin -= static_cast<LONG>(removedCharacters);
160 selection.cpMax -= static_cast<LONG>(removedCharacters);
161 }
162
163 m_numCharacters -= removedCharacters;
164 }
165
166 // append message
167 m_buffer.push_back(msg);
168 size_t newNumCharacters = m_numCharacters + m_buffer.back().size();
169
170 // add line to bottom of control
171 if (newNumCharacters > m_maxCharacters) {
172 m_maxCharacters = newNumCharacters;
173 SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
174 }
175 CHARRANGE range;
176 range.cpMin = m_numCharacters;
177 range.cpMax = m_numCharacters;
178 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
179 SendMessage(m_hwnd, EM_REPLACESEL, FALSE,
180 reinterpret_cast<LPARAM>(m_buffer.back().c_str()));
181
182 // adjust selection
183 bool atEnd = false;
184 if (selection.cpMax == static_cast<LONG>(m_numCharacters)) {
185 selection.cpMin = static_cast<LONG>(newNumCharacters);
186 selection.cpMax = static_cast<LONG>(newNumCharacters);
187 atEnd = true;
188 }
189
190 // restore the selection
191 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&selection));
192 if (atEnd) {
193 SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0);
194 }
195
196 if (wasEmpty && m_show) {
197 ShowWindow(m_frame, TRUE);
198 }
199
200 m_numCharacters = newNumCharacters;
201}
202
203void
204CArchConsoleWindows::setSize(int width, int height)
205{
206 DWORD style = GetWindowLong(m_frame, GWL_STYLE);
207 DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
208 RECT rect;
209 rect.left = 100;
210 rect.top = 100;
211 rect.right = rect.left + width * m_wChar;
212 rect.bottom = rect.top + height * m_hChar;
213 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
214 SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left,
215 rect.bottom - rect.top,
216 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
217}
218
219LRESULT
220CArchConsoleWindows::wndProc(HWND hwnd,
221 UINT msg, WPARAM wParam, LPARAM lParam)
222{
223 switch (msg) {
224 case WM_CLOSE:
225 ShowWindow(m_frame, FALSE);
226 m_show = false;
227 return 0;
228
229 case SYNERGY_MSG_CONSOLE_OPEN:
230 return 0;
231
232 case SYNERGY_MSG_CONSOLE_CLOSE:
233 SendMessage(m_frame, WM_CLOSE, 0, 0);
234 m_show = false;
235 return 0;
236
237 case SYNERGY_MSG_CONSOLE_SHOW:
238 m_show = true;
239 if (wParam != 0 || !m_buffer.empty()) {
240 ShowWindow(m_frame, TRUE);
241 }
242 return 0;
243
244 case SYNERGY_MSG_CONSOLE_WRITE:
245 appendBuffer(reinterpret_cast<const char*>(wParam));
246 return 0;
247
248 case SYNERGY_MSG_CONSOLE_CLEAR:
249 clearBuffer();
250 return 0;
251
252 case WM_SIZE:
253 if (hwnd == m_frame) {
254 MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
255 }
256 break;
257
258 case WM_SIZING:
259 if (hwnd == m_frame) {
260 // get window vs client area info
261 int wBase = 40 * m_wChar;
262 int hBase = 40 * m_hChar;
263 DWORD style = GetWindowLong(m_frame, GWL_STYLE);
264 DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
265 RECT rect;
266 rect.left = 100;
267 rect.top = 100;
268 rect.right = rect.left + wBase;
269 rect.bottom = rect.top + hBase;
270 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
271 wBase = rect.right - rect.left - wBase;
272 hBase = rect.bottom - rect.top - hBase;
273
274 // get closest size that's a multiple of the character size
275 RECT* newRect = (RECT*)lParam;
276 int width = (newRect->right - newRect->left - wBase) / m_wChar;
277 int height = (newRect->bottom - newRect->top - hBase) / m_hChar;
278 width = width * m_wChar + wBase;
279 height = height * m_hChar + hBase;
280
281 // adjust sizing rect
282 switch (wParam) {
283 case WMSZ_LEFT:
284 case WMSZ_TOPLEFT:
285 case WMSZ_BOTTOMLEFT:
286 newRect->left = newRect->right - width;
287 break;
288
289 case WMSZ_RIGHT:
290 case WMSZ_TOPRIGHT:
291 case WMSZ_BOTTOMRIGHT:
292 newRect->right = newRect->left + width;
293 break;
294 }
295 switch (wParam) {
296 case WMSZ_TOP:
297 case WMSZ_TOPLEFT:
298 case WMSZ_TOPRIGHT:
299 newRect->top = newRect->bottom - height;
300 break;
301
302 case WMSZ_BOTTOM:
303 case WMSZ_BOTTOMLEFT:
304 case WMSZ_BOTTOMRIGHT:
305 newRect->bottom = newRect->top + height;
306 break;
307 }
308 return TRUE;
309 }
310 break;
311
312 default:
313 break;
314 }
315
316 return DefWindowProc(hwnd, msg, wParam, lParam);
317}
318
319LRESULT CALLBACK
320CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg,
321 WPARAM wParam, LPARAM lParam)
322{
323 // forward the message
324 if (s_instance != NULL) {
325 return s_instance->wndProc(hwnd, msg, wParam, lParam);
326 }
327 else {
328 return DefWindowProc(hwnd, msg, wParam, lParam);
329 }
330}
331
332void
333CArchConsoleWindows::threadMainLoop()
334{
335 LoadLibrary("RICHED32.DLL");
336
337 // get the app icons
338 HICON largeIcon, smallIcon;
339 CArchMiscWindows::getIcons(largeIcon, smallIcon);
340
341 // register a window class
342 WNDCLASSEX classInfo;
343 classInfo.cbSize = sizeof(classInfo);
344 classInfo.style = 0;
345 classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc;
346 classInfo.cbClsExtra = 0;
347 classInfo.cbWndExtra = sizeof(CArchConsoleWindows*);
348 classInfo.hInstance = s_appInstance;
349 classInfo.hIcon = largeIcon;
350 classInfo.hCursor = NULL;
351 classInfo.hbrBackground = NULL;
352 classInfo.lpszMenuName = NULL;
353 classInfo.lpszClassName = TEXT("SynergyConsole");
354 classInfo.hIconSm = smallIcon;
355 ATOM windowClass = RegisterClassEx(&classInfo);
356
357 // create frame window
358 m_frame = CreateWindowEx(0,
359 reinterpret_cast<LPCTSTR>(windowClass),
360 TEXT("Synergy Log"),
361 WS_OVERLAPPEDWINDOW,
362 CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
363 NULL,
364 NULL,
365 s_appInstance,
366 NULL);
367
368 // create log window
369 m_hwnd = CreateWindowEx(0,
370 "RichEdit",
371 TEXT(""),
372 WS_CHILD | WS_VISIBLE | WS_VSCROLL |
373 ES_MULTILINE | ES_READONLY,
374 0, 0, 1, 1,
375 m_frame,
376 (HMENU)1,
377 s_appInstance,
378 NULL);
379
380 // select font and get info
381 HDC hdc = GetDC(m_hwnd);
382 HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
383 TEXTMETRIC metrics;
384 GetTextMetrics(hdc, &metrics);
385 CHARFORMAT format;
386 format.cbSize = sizeof(format);
387 format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE |
388 CFM_OFFSET | CFM_SIZE | CFM_PROTECTED |
389 CFM_BOLD | CFM_ITALIC |
390 CFM_STRIKEOUT | CFM_UNDERLINE;
391 format.dwEffects = 0;
392 format.yHeight = metrics.tmHeight;
393 format.yOffset = 0;
394 format.crTextColor = RGB(0, 0, 0);
395 format.bCharSet = DEFAULT_CHARSET;
396 format.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
397 GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName);
398 SelectObject(hdc, oldFont);
399 ReleaseDC(m_hwnd, hdc);
400
401 // prep window
402 SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
403 SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast<LPARAM>(&format));
404 SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255));
405 m_wChar = metrics.tmAveCharWidth;
406 m_hChar = metrics.tmHeight + metrics.tmExternalLeading;
407 setSize(80, 25);
408
409 // signal ready
410 ARCH->lockMutex(m_mutex);
411 m_ready = true;
412 ARCH->broadcastCondVar(m_condVar);
413 ARCH->unlockMutex(m_mutex);
414
415 // handle failure
416 if (m_hwnd == NULL) {
417 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
418 return;
419 }
420
421 // main loop
422 MSG msg;
423 while (GetMessage(&msg, NULL, 0, 0)) {
424 TranslateMessage(&msg);
425 DispatchMessage(&msg);
426 }
427
428 // clean up
429 DestroyWindow(m_hwnd);
430 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
431}
432
433void*
434CArchConsoleWindows::threadEntry(void* self)
435{
436 reinterpret_cast<CArchConsoleWindows*>(self)->threadMainLoop();
437 return NULL;
438}
Note: See TracBrowser for help on using the repository browser.