00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "CArchConsoleWindows.h"
00016 #include "IArchMultithread.h"
00017 #include "CArch.h"
00018 #include "CArchMiscWindows.h"
00019 #include <richedit.h>
00020
00021 #define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021
00022 #define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022
00023 #define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023
00024 #define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024
00025 #define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025
00026 #define TWIPS_PER_POINT 20
00027
00028
00029
00030
00031
00032 CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL;
00033 HINSTANCE CArchConsoleWindows::s_appInstance = NULL;
00034
00035 CArchConsoleWindows::CArchConsoleWindows(void* appInstance) :
00036 m_show(false),
00037 m_maxLines(1000),
00038 m_numCharacters(0),
00039 m_maxCharacters(65536)
00040 {
00041
00042 s_instance = this;
00043
00044
00045 s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
00046
00047
00048 m_mutex = ARCH->newMutex();
00049
00050
00051 m_ready = false;
00052 m_condVar = ARCH->newCondVar();
00053
00054
00055
00056
00057 ARCH->lockMutex(m_mutex);
00058
00059
00060
00061
00062
00063
00064 m_thread = ARCH->newThread(&CArchConsoleWindows::threadEntry, this);
00065
00066
00067 while (!m_ready) {
00068 ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
00069 }
00070
00071
00072 ARCH->unlockMutex(m_mutex);
00073
00074 }
00075
00076 CArchConsoleWindows::~CArchConsoleWindows()
00077 {
00078 if (m_thread != NULL) {
00079 PostMessage(m_hwnd, WM_QUIT, 0, 0);
00080 ARCH->wait(m_thread, -1.0);
00081 ARCH->closeThread(m_thread);
00082 }
00083 ARCH->closeCondVar(m_condVar);
00084 ARCH->closeMutex(m_mutex);
00085 s_instance = NULL;
00086 }
00087
00088 void
00089 CArchConsoleWindows::openConsole(const char* title)
00090 {
00091 SetWindowText(m_frame, title);
00092 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0);
00093 }
00094
00095 void
00096 CArchConsoleWindows::closeConsole()
00097 {
00098 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0);
00099 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0);
00100 }
00101
00102 void
00103 CArchConsoleWindows::showConsole(bool showIfEmpty)
00104 {
00105 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0);
00106 }
00107
00108 void
00109 CArchConsoleWindows::writeConsole(const char* str)
00110 {
00111 SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE,
00112 reinterpret_cast<WPARAM>(str), 0);
00113 }
00114
00115 const char*
00116 CArchConsoleWindows::getNewlineForConsole()
00117 {
00118 return "\r\n";
00119 }
00120
00121 void
00122 CArchConsoleWindows::clearBuffer()
00123 {
00124 m_buffer.clear();
00125 m_numCharacters = 0;
00126 SetWindowText(m_hwnd, "");
00127 }
00128
00129 void
00130 CArchConsoleWindows::appendBuffer(const char* msg)
00131 {
00132 bool wasEmpty = m_buffer.empty();
00133
00134
00135 CHARRANGE selection;
00136 SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast<LPARAM>(&selection));
00137
00138
00139 size_t removedCharacters = 0;
00140 while (m_buffer.size() >= m_maxLines) {
00141 removedCharacters += m_buffer.front().size();
00142 m_buffer.pop_front();
00143 }
00144
00145
00146 if (removedCharacters > 0) {
00147 CHARRANGE range;
00148 range.cpMin = 0;
00149 range.cpMax = static_cast<LONG>(removedCharacters);
00150 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
00151 SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast<LPARAM>(""));
00152
00153
00154 if (selection.cpMin < static_cast<LONG>(removedCharacters) ||
00155 selection.cpMax < static_cast<LONG>(removedCharacters)) {
00156 selection.cpMin = 0;
00157 selection.cpMax = 0;
00158 }
00159 else {
00160 selection.cpMin -= static_cast<LONG>(removedCharacters);
00161 selection.cpMax -= static_cast<LONG>(removedCharacters);
00162 }
00163
00164 m_numCharacters -= removedCharacters;
00165 }
00166
00167
00168 m_buffer.push_back(msg);
00169 size_t newNumCharacters = m_numCharacters + m_buffer.back().size();
00170
00171
00172 if (newNumCharacters > m_maxCharacters) {
00173 m_maxCharacters = newNumCharacters;
00174 SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
00175 }
00176 CHARRANGE range;
00177 range.cpMin = m_numCharacters;
00178 range.cpMax = m_numCharacters;
00179 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
00180 SendMessage(m_hwnd, EM_REPLACESEL, FALSE,
00181 reinterpret_cast<LPARAM>(m_buffer.back().c_str()));
00182
00183
00184 bool atEnd = false;
00185 if (selection.cpMax == static_cast<LONG>(m_numCharacters)) {
00186 selection.cpMin = static_cast<LONG>(newNumCharacters);
00187 selection.cpMax = static_cast<LONG>(newNumCharacters);
00188 atEnd = true;
00189 }
00190
00191
00192 SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&selection));
00193 if (atEnd) {
00194 SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0);
00195 }
00196
00197 if (wasEmpty && m_show) {
00198 ShowWindow(m_frame, TRUE);
00199 }
00200
00201 m_numCharacters = newNumCharacters;
00202 }
00203
00204 void
00205 CArchConsoleWindows::setSize(int width, int height)
00206 {
00207 DWORD style = GetWindowLong(m_frame, GWL_STYLE);
00208 DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
00209 RECT rect;
00210 rect.left = 100;
00211 rect.top = 100;
00212 rect.right = rect.left + width * m_wChar;
00213 rect.bottom = rect.top + height * m_hChar;
00214 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
00215 SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left,
00216 rect.bottom - rect.top,
00217 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
00218 }
00219
00220 LRESULT
00221 CArchConsoleWindows::wndProc(HWND hwnd,
00222 UINT msg, WPARAM wParam, LPARAM lParam)
00223 {
00224 switch (msg) {
00225 case WM_CLOSE:
00226 ShowWindow(m_frame, FALSE);
00227 m_show = false;
00228 return 0;
00229
00230 case SYNERGY_MSG_CONSOLE_OPEN:
00231 return 0;
00232
00233 case SYNERGY_MSG_CONSOLE_CLOSE:
00234 SendMessage(m_frame, WM_CLOSE, 0, 0);
00235 m_show = false;
00236 return 0;
00237
00238 case SYNERGY_MSG_CONSOLE_SHOW:
00239 m_show = true;
00240 if (wParam != 0 || !m_buffer.empty()) {
00241 ShowWindow(m_frame, TRUE);
00242 }
00243 return 0;
00244
00245 case SYNERGY_MSG_CONSOLE_WRITE:
00246 appendBuffer(reinterpret_cast<const char*>(wParam));
00247 return 0;
00248
00249 case SYNERGY_MSG_CONSOLE_CLEAR:
00250 clearBuffer();
00251 return 0;
00252
00253 case WM_SIZE:
00254 if (hwnd == m_frame) {
00255 MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
00256 }
00257 break;
00258
00259 case WM_SIZING:
00260 if (hwnd == m_frame) {
00261
00262 int wBase = 40 * m_wChar;
00263 int hBase = 40 * m_hChar;
00264 DWORD style = GetWindowLong(m_frame, GWL_STYLE);
00265 DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE);
00266 RECT rect;
00267 rect.left = 100;
00268 rect.top = 100;
00269 rect.right = rect.left + wBase;
00270 rect.bottom = rect.top + hBase;
00271 AdjustWindowRectEx(&rect, style, FALSE, exStyle);
00272 wBase = rect.right - rect.left - wBase;
00273 hBase = rect.bottom - rect.top - hBase;
00274
00275
00276 RECT* newRect = (RECT*)lParam;
00277 int width = (newRect->right - newRect->left - wBase) / m_wChar;
00278 int height = (newRect->bottom - newRect->top - hBase) / m_hChar;
00279 width = width * m_wChar + wBase;
00280 height = height * m_hChar + hBase;
00281
00282
00283 switch (wParam) {
00284 case WMSZ_LEFT:
00285 case WMSZ_TOPLEFT:
00286 case WMSZ_BOTTOMLEFT:
00287 newRect->left = newRect->right - width;
00288 break;
00289
00290 case WMSZ_RIGHT:
00291 case WMSZ_TOPRIGHT:
00292 case WMSZ_BOTTOMRIGHT:
00293 newRect->right = newRect->left + width;
00294 break;
00295 }
00296 switch (wParam) {
00297 case WMSZ_TOP:
00298 case WMSZ_TOPLEFT:
00299 case WMSZ_TOPRIGHT:
00300 newRect->top = newRect->bottom - height;
00301 break;
00302
00303 case WMSZ_BOTTOM:
00304 case WMSZ_BOTTOMLEFT:
00305 case WMSZ_BOTTOMRIGHT:
00306 newRect->bottom = newRect->top + height;
00307 break;
00308 }
00309 return TRUE;
00310 }
00311 break;
00312
00313 default:
00314 break;
00315 }
00316
00317 return DefWindowProc(hwnd, msg, wParam, lParam);
00318 }
00319
00320 LRESULT CALLBACK
00321 CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg,
00322 WPARAM wParam, LPARAM lParam)
00323 {
00324
00325 if (s_instance != NULL) {
00326 return s_instance->wndProc(hwnd, msg, wParam, lParam);
00327 }
00328 else {
00329 return DefWindowProc(hwnd, msg, wParam, lParam);
00330 }
00331 }
00332
00333 void
00334 CArchConsoleWindows::threadMainLoop()
00335 {
00336 LoadLibrary("RICHED32.DLL");
00337
00338
00339 HICON largeIcon, smallIcon;
00340 CArchMiscWindows::getIcons(largeIcon, smallIcon);
00341
00342
00343 WNDCLASSEX classInfo;
00344 classInfo.cbSize = sizeof(classInfo);
00345 classInfo.style = 0;
00346 classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc;
00347 classInfo.cbClsExtra = 0;
00348 classInfo.cbWndExtra = sizeof(CArchConsoleWindows*);
00349 classInfo.hInstance = s_appInstance;
00350 classInfo.hIcon = largeIcon;
00351 classInfo.hCursor = NULL;
00352 classInfo.hbrBackground = NULL;
00353 classInfo.lpszMenuName = NULL;
00354 classInfo.lpszClassName = TEXT("SynergyConsole");
00355 classInfo.hIconSm = smallIcon;
00356 ATOM windowClass = RegisterClassEx(&classInfo);
00357
00358
00359 m_frame = CreateWindowEx(0,
00360 reinterpret_cast<LPCTSTR>(windowClass),
00361 TEXT("Synergy Log"),
00362 WS_OVERLAPPEDWINDOW,
00363 CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
00364 NULL,
00365 NULL,
00366 s_appInstance,
00367 NULL);
00368
00369
00370 m_hwnd = CreateWindowEx(0,
00371 "RichEdit",
00372 TEXT(""),
00373 WS_CHILD | WS_VISIBLE | WS_VSCROLL |
00374 ES_MULTILINE | ES_READONLY,
00375 0, 0, 1, 1,
00376 m_frame,
00377 (HMENU)1,
00378 s_appInstance,
00379 NULL);
00380
00381
00382 HDC hdc = GetDC(m_hwnd);
00383 HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
00384 TEXTMETRIC metrics;
00385 GetTextMetrics(hdc, &metrics);
00386 CHARFORMAT format;
00387 format.cbSize = sizeof(format);
00388 format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE |
00389 CFM_OFFSET | CFM_SIZE | CFM_PROTECTED |
00390 CFM_BOLD | CFM_ITALIC |
00391 CFM_STRIKEOUT | CFM_UNDERLINE;
00392 format.dwEffects = 0;
00393 format.yHeight = metrics.tmHeight * TWIPS_PER_POINT;
00394 format.yOffset = 0;
00395 format.crTextColor = RGB(0, 0, 0);
00396 format.bCharSet = DEFAULT_CHARSET;
00397 format.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
00398 GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName);
00399 SelectObject(hdc, oldFont);
00400 ReleaseDC(m_hwnd, hdc);
00401
00402
00403 SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters);
00404 SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast<LPARAM>(&format));
00405 SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255));
00406 m_wChar = metrics.tmAveCharWidth;
00407 m_hChar = metrics.tmHeight + metrics.tmExternalLeading;
00408 setSize(80, 25);
00409
00410
00411 ARCH->lockMutex(m_mutex);
00412 m_ready = true;
00413 ARCH->broadcastCondVar(m_condVar);
00414 ARCH->unlockMutex(m_mutex);
00415
00416
00417 if (m_hwnd == NULL) {
00418 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
00419 return;
00420 }
00421
00422
00423 MSG msg;
00424 while (GetMessage(&msg, NULL, 0, 0)) {
00425 TranslateMessage(&msg);
00426 DispatchMessage(&msg);
00427 }
00428
00429
00430 DestroyWindow(m_hwnd);
00431 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
00432 }
00433
00434 void*
00435 CArchConsoleWindows::threadEntry(void* self)
00436 {
00437 reinterpret_cast<CArchConsoleWindows*>(self)->threadMainLoop();
00438 return NULL;
00439 }