1/* 2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 *"Software"), to deal in the Software without restriction, including 7 *without limitation the rights to use, copy, modify, merge, publish, 8 *distribute, sublicense, and/or sell copies of the Software, and to 9 *permit persons to whom the Software is furnished to do so, subject to 10 *the following conditions: 11 * 12 *The above copyright notice and this permission notice shall be 13 *included in all copies or substantial portions of the Software. 14 * 15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR 19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 *Except as contained in this notice, the name of Harold L Hunt II 24 *shall not be used in advertising or otherwise to promote the sale, use 25 *or other dealings in this Software without prior written authorization 26 *from Harold L Hunt II. 27 * 28 * Authors: Harold L Hunt II 29 * Earle F. Philhower III 30 */ 31 32#ifdef HAVE_XWIN_CONFIG_H 33#include <xwin-config.h> 34#endif 35#include "win.h" 36#include <shellapi.h> 37#include "winprefs.h" 38 39/* 40 * Local function prototypes 41 */ 42 43static INT_PTR CALLBACK 44winExitDlgProc(HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam); 45 46static INT_PTR CALLBACK 47winChangeDepthDlgProc(HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam); 48 49static INT_PTR CALLBACK 50winAboutDlgProc(HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam); 51 52static void 53 winDrawURLWindow(LPARAM lParam); 54 55static LRESULT CALLBACK 56winURLWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 57 58static void 59 winOverrideURLButton(HWND hdlg, int id); 60 61static void 62 winUnoverrideURLButton(HWND hdlg, int id); 63 64/* 65 * Owner-draw a button as a URL 66 */ 67 68static void 69winDrawURLWindow(LPARAM lParam) 70{ 71 DRAWITEMSTRUCT *draw; 72 char str[256]; 73 RECT rect; 74 HFONT font; 75 COLORREF crText; 76 77 draw = (DRAWITEMSTRUCT *) lParam; 78 GetWindowText(draw->hwndItem, str, sizeof(str)); 79 str[255] = 0; 80 GetClientRect(draw->hwndItem, &rect); 81 82 /* Color the button depending upon its state */ 83 if (draw->itemState & ODS_SELECTED) 84 crText = RGB(128 + 64, 0, 0); 85 else if (draw->itemState & ODS_FOCUS) 86 crText = RGB(0, 128 + 64, 0); 87 else 88 crText = RGB(0, 0, 128 + 64); 89 SetTextColor(draw->hDC, crText); 90 91 /* Create font 8 high, standard dialog font */ 92 font = CreateFont(-8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, 93 0, 0, 0, 0, 0, "MS Sans Serif"); 94 if (!font) { 95 ErrorF("winDrawURLWindow: Unable to create URL font, bailing.\n"); 96 return; 97 } 98 /* Draw it */ 99 SetBkMode(draw->hDC, OPAQUE); 100 SelectObject(draw->hDC, font); 101 DrawText(draw->hDC, str, strlen(str), &rect, DT_LEFT | DT_VCENTER); 102 /* Delete the created font, replace it with stock font */ 103 DeleteObject(SelectObject(draw->hDC, GetStockObject(ANSI_VAR_FONT))); 104} 105 106/* 107 * WndProc for overridden buttons 108 */ 109 110static LRESULT CALLBACK 111winURLWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 112{ 113 WNDPROC origCB = NULL; 114 HCURSOR cursor; 115 116 /* If it's a SetCursor message, tell it to the hand */ 117 if (msg == WM_SETCURSOR) { 118 cursor = LoadCursor(NULL, IDC_HAND); 119 if (cursor) 120 SetCursor(cursor); 121 return TRUE; 122 } 123 origCB = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_USERDATA); 124 /* Otherwise fall through to original WndProc */ 125 if (origCB) 126 return CallWindowProc(origCB, hwnd, msg, wParam, lParam); 127 else 128 return FALSE; 129} 130 131/* 132 * Register and unregister the custom WndProc 133 */ 134 135static void 136winOverrideURLButton(HWND hwnd, int id) 137{ 138 WNDPROC origCB; 139 140 origCB = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwnd, id), 141 GWLP_WNDPROC, (LONG_PTR) winURLWndProc); 142 SetWindowLongPtr(GetDlgItem(hwnd, id), GWLP_USERDATA, (LONG_PTR) origCB); 143} 144 145static void 146winUnoverrideURLButton(HWND hwnd, int id) 147{ 148 WNDPROC origCB; 149 150 origCB = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwnd, id), GWLP_USERDATA, 0); 151 if (origCB) 152 SetWindowLongPtr(GetDlgItem(hwnd, id), GWLP_WNDPROC, (LONG_PTR) origCB); 153} 154 155/* 156 * Center a dialog window in the desktop window 157 * and set small and large icons to X icons. 158 */ 159 160static void 161winInitDialog(HWND hwndDlg) 162{ 163 HWND hwndDesk; 164 RECT rc, rcDlg, rcDesk; 165 HICON hIcon, hIconSmall; 166 167 hwndDesk = GetParent(hwndDlg); 168 if (!hwndDesk || IsIconic(hwndDesk)) 169 hwndDesk = GetDesktopWindow(); 170 171 /* Remove minimize and maximize buttons */ 172 SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) 173 & ~(WS_MAXIMIZEBOX | WS_MINIMIZEBOX)); 174 175 /* Set Window not to show in the task bar */ 176 SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, 177 GetWindowLongPtr(hwndDlg, GWL_EXSTYLE) & ~WS_EX_APPWINDOW); 178 179 /* Center dialog window in the screen. Not done for multi-monitor systems, where 180 * it is likely to end up split across the screens. In that case, it appears 181 * near the Tray icon. 182 */ 183 if (GetSystemMetrics(SM_CMONITORS) > 1) { 184 /* Still need to refresh the frame change. */ 185 SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, 186 SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); 187 } 188 else { 189 GetWindowRect(hwndDesk, &rcDesk); 190 GetWindowRect(hwndDlg, &rcDlg); 191 CopyRect(&rc, &rcDesk); 192 193 OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); 194 OffsetRect(&rc, -rc.left, -rc.top); 195 OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); 196 197 SetWindowPos(hwndDlg, 198 HWND_TOPMOST, 199 rcDesk.left + (rc.right / 2), 200 rcDesk.top + (rc.bottom / 2), 201 0, 0, SWP_NOSIZE | SWP_FRAMECHANGED); 202 } 203 204 if (g_hIconX) 205 hIcon = g_hIconX; 206 else 207 hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_XWIN)); 208 209 if (g_hSmallIconX) 210 hIconSmall = g_hSmallIconX; 211 else 212 hIconSmall = LoadImage(g_hInstance, 213 MAKEINTRESOURCE(IDI_XWIN), IMAGE_ICON, 214 GetSystemMetrics(SM_CXSMICON), 215 GetSystemMetrics(SM_CYSMICON), LR_SHARED); 216 217 PostMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon); 218 PostMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIconSmall); 219} 220 221/* 222 * Display the Exit dialog box 223 */ 224 225void 226winDisplayExitDialog(winPrivScreenPtr pScreenPriv) 227{ 228 int i; 229 int liveClients = 0; 230 231 /* Count up running clients (clients[0] is serverClient) */ 232 for (i = 1; i < currentMaxClients; i++) 233 if (clients[i] != NullClient) 234 liveClients++; 235 /* Count down server internal clients */ 236 if (pScreenPriv->pScreenInfo->fMultiWindow) 237 liveClients -= 2; /* multiwindow window manager & XMsgProc */ 238 if (g_fClipboardStarted) 239 liveClients--; /* clipboard manager */ 240 241 /* A user reported that this sometimes drops below zero. just eye-candy. */ 242 if (liveClients < 0) 243 liveClients = 0; 244 245 /* Don't show the exit confirmation dialog if SilentExit & no clients, 246 or ForceExit, is enabled */ 247 if ((pref.fSilentExit && liveClients <= 0) || pref.fForceExit) { 248 if (g_hDlgExit != NULL) { 249 DestroyWindow(g_hDlgExit); 250 g_hDlgExit = NULL; 251 } 252 PostMessage(pScreenPriv->hwndScreen, WM_GIVEUP, 0, 0); 253 return; 254 } 255 256 pScreenPriv->iConnectedClients = liveClients; 257 258 /* Check if dialog already exists */ 259 if (g_hDlgExit != NULL) { 260 /* Dialog box already exists, display it */ 261 ShowWindow(g_hDlgExit, SW_SHOWDEFAULT); 262 263 /* User has lost the dialog. Show them where it is. */ 264 SetForegroundWindow(g_hDlgExit); 265 266 return; 267 } 268 269 /* Create dialog box */ 270 g_hDlgExit = CreateDialogParam(g_hInstance, 271 "EXIT_DIALOG", 272 pScreenPriv->hwndScreen, 273 winExitDlgProc, (LPARAM) pScreenPriv); 274 275 /* Show the dialog box */ 276 ShowWindow(g_hDlgExit, SW_SHOW); 277 278 /* Needed to get keyboard controls (tab, arrows, enter, esc) to work */ 279 SetForegroundWindow(g_hDlgExit); 280 281 /* Set focus to the Cancel button */ 282 PostMessage(g_hDlgExit, WM_NEXTDLGCTL, 283 (WPARAM) GetDlgItem(g_hDlgExit, IDCANCEL), TRUE); 284} 285 286#define CONNECTED_CLIENTS_FORMAT "There %s currently %d client%s connected." 287 288/* 289 * Exit dialog window procedure 290 */ 291 292static INT_PTR CALLBACK 293winExitDlgProc(HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam) 294{ 295 static winPrivScreenPtr s_pScreenPriv = NULL; 296 297 /* Branch on message type */ 298 switch (message) { 299 case WM_INITDIALOG: 300 { 301 char *pszConnectedClients; 302 303 /* Store pointers to private structures for future use */ 304 s_pScreenPriv = (winPrivScreenPtr) lParam; 305 306 winInitDialog(hDialog); 307 308 /* Format the connected clients string */ 309 if (asprintf(&pszConnectedClients, CONNECTED_CLIENTS_FORMAT, 310 (s_pScreenPriv->iConnectedClients == 1) ? "is" : "are", 311 s_pScreenPriv->iConnectedClients, 312 (s_pScreenPriv->iConnectedClients == 1) ? "" : "s") == -1) 313 return TRUE; 314 315 /* Set the number of connected clients */ 316 SetWindowText(GetDlgItem(hDialog, IDC_CLIENTS_CONNECTED), 317 pszConnectedClients); 318 free(pszConnectedClients); 319 } 320 return TRUE; 321 322 case WM_COMMAND: 323 switch (LOWORD(wParam)) { 324 case IDOK: 325 /* Send message to call the GiveUp function */ 326 PostMessage(s_pScreenPriv->hwndScreen, WM_GIVEUP, 0, 0); 327 DestroyWindow(g_hDlgExit); 328 g_hDlgExit = NULL; 329 330 /* Fix to make sure keyboard focus isn't trapped */ 331 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 332 return TRUE; 333 334 case IDCANCEL: 335 DestroyWindow(g_hDlgExit); 336 g_hDlgExit = NULL; 337 338 /* Fix to make sure keyboard focus isn't trapped */ 339 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 340 return TRUE; 341 } 342 break; 343 344 case WM_MOUSEMOVE: 345 case WM_NCMOUSEMOVE: 346 /* Show the cursor if it is hidden */ 347 if (g_fSoftwareCursor && !g_fCursor) { 348 g_fCursor = TRUE; 349 ShowCursor(TRUE); 350 } 351 return TRUE; 352 353 case WM_CLOSE: 354 DestroyWindow(g_hDlgExit); 355 g_hDlgExit = NULL; 356 357 /* Fix to make sure keyboard focus isn't trapped */ 358 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 359 return TRUE; 360 } 361 362 return FALSE; 363} 364 365/* 366 * Display the Depth Change dialog box 367 */ 368 369void 370winDisplayDepthChangeDialog(winPrivScreenPtr pScreenPriv) 371{ 372 /* Check if dialog already exists */ 373 if (g_hDlgDepthChange != NULL) { 374 /* Dialog box already exists, display it */ 375 ShowWindow(g_hDlgDepthChange, SW_SHOWDEFAULT); 376 377 /* User has lost the dialog. Show them where it is. */ 378 SetForegroundWindow(g_hDlgDepthChange); 379 380 return; 381 } 382 383 /* 384 * Display a notification to the user that the visual 385 * will not be displayed until the Windows display depth 386 * is restored to the original value. 387 */ 388 g_hDlgDepthChange = CreateDialogParam(g_hInstance, 389 "DEPTH_CHANGE_BOX", 390 pScreenPriv->hwndScreen, 391 winChangeDepthDlgProc, 392 (LPARAM) pScreenPriv); 393 /* Show the dialog box */ 394 ShowWindow(g_hDlgDepthChange, SW_SHOW); 395 396 if (!g_hDlgDepthChange) 397 ErrorF("winDisplayDepthChangeDialog - GetLastError: %d\n", 398 (int) GetLastError()); 399 400 /* Minimize the display window */ 401 ShowWindow(pScreenPriv->hwndScreen, SW_MINIMIZE); 402} 403 404/* 405 * Process messages for the dialog that is displayed for 406 * disruptive screen depth changes. 407 */ 408 409static INT_PTR CALLBACK 410winChangeDepthDlgProc(HWND hwndDialog, UINT message, 411 WPARAM wParam, LPARAM lParam) 412{ 413 static winPrivScreenPtr s_pScreenPriv = NULL; 414 static winScreenInfo *s_pScreenInfo = NULL; 415 416#if CYGDEBUG 417 winDebug("winChangeDepthDlgProc\n"); 418#endif 419 420 /* Branch on message type */ 421 switch (message) { 422 case WM_INITDIALOG: 423#if CYGDEBUG 424 winDebug("winChangeDepthDlgProc - WM_INITDIALOG\n"); 425#endif 426 427 /* Store pointers to private structures for future use */ 428 s_pScreenPriv = (winPrivScreenPtr) lParam; 429 s_pScreenInfo = s_pScreenPriv->pScreenInfo; 430 431#if CYGDEBUG 432 winDebug("winChangeDepthDlgProc - WM_INITDIALOG - s_pScreenPriv: %p, " 433 "s_pScreenInfo: %p\n", 434 s_pScreenPriv, s_pScreenInfo); 435#endif 436 437#if CYGDEBUG 438 winDebug("winChangeDepthDlgProc - WM_INITDIALOG - orig bpp: %u, " 439 "current bpp: %d\n", 440 (unsigned int)s_pScreenInfo->dwBPP, 441 GetDeviceCaps(s_pScreenPriv->hdcScreen, BITSPIXEL)); 442#endif 443 444 winInitDialog(hwndDialog); 445 446 return TRUE; 447 448 case WM_DISPLAYCHANGE: 449#if CYGDEBUG 450 winDebug("winChangeDepthDlgProc - WM_DISPLAYCHANGE - orig bpp: %u, " 451 "new bpp: %d\n", 452 (unsigned int)s_pScreenInfo->dwBPP, 453 GetDeviceCaps(s_pScreenPriv->hdcScreen, BITSPIXEL)); 454#endif 455 456 /* Dismiss the dialog if the display returns to the original depth */ 457 if (GetDeviceCaps(s_pScreenPriv->hdcScreen, BITSPIXEL) == 458 s_pScreenInfo->dwBPP) { 459 ErrorF("winChangeDelthDlgProc - wParam == s_pScreenInfo->dwBPP\n"); 460 461 /* Depth has been restored, dismiss dialog */ 462 DestroyWindow(g_hDlgDepthChange); 463 g_hDlgDepthChange = NULL; 464 465 /* Flag that we have a valid screen depth */ 466 s_pScreenPriv->fBadDepth = FALSE; 467 } 468 return TRUE; 469 470 case WM_COMMAND: 471 switch (LOWORD(wParam)) { 472 case IDOK: 473 case IDCANCEL: 474 winDebug("winChangeDepthDlgProc - WM_COMMAND - IDOK or IDCANCEL\n"); 475 476 /* 477 * User dismissed the dialog, hide it until the 478 * display mode is restored. 479 */ 480 ShowWindow(g_hDlgDepthChange, SW_HIDE); 481 return TRUE; 482 } 483 break; 484 485 case WM_CLOSE: 486 winDebug("winChangeDepthDlgProc - WM_CLOSE\n"); 487 488 DestroyWindow(g_hDlgAbout); 489 g_hDlgAbout = NULL; 490 491 /* Fix to make sure keyboard focus isn't trapped */ 492 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 493 return TRUE; 494 } 495 496 return FALSE; 497} 498 499/* 500 * Display the About dialog box 501 */ 502 503void 504winDisplayAboutDialog(winPrivScreenPtr pScreenPriv) 505{ 506 /* Check if dialog already exists */ 507 if (g_hDlgAbout != NULL) { 508 /* Dialog box already exists, display it */ 509 ShowWindow(g_hDlgAbout, SW_SHOWDEFAULT); 510 511 /* User has lost the dialog. Show them where it is. */ 512 SetForegroundWindow(g_hDlgAbout); 513 514 return; 515 } 516 517 /* 518 * Display the about box 519 */ 520 g_hDlgAbout = CreateDialogParam(g_hInstance, 521 "ABOUT_BOX", 522 pScreenPriv->hwndScreen, 523 winAboutDlgProc, (LPARAM) pScreenPriv); 524 525 /* Show the dialog box */ 526 ShowWindow(g_hDlgAbout, SW_SHOW); 527 528 /* Needed to get keyboard controls (tab, arrows, enter, esc) to work */ 529 SetForegroundWindow(g_hDlgAbout); 530 531 /* Set focus to the OK button */ 532 PostMessage(g_hDlgAbout, WM_NEXTDLGCTL, 533 (WPARAM) GetDlgItem(g_hDlgAbout, IDOK), TRUE); 534} 535 536/* 537 * Process messages for the about dialog. 538 */ 539 540static INT_PTR CALLBACK 541winAboutDlgProc(HWND hwndDialog, UINT message, WPARAM wParam, LPARAM lParam) 542{ 543 static winPrivScreenPtr s_pScreenPriv = NULL; 544 545#if CYGDEBUG 546 winDebug("winAboutDlgProc\n"); 547#endif 548 549 /* Branch on message type */ 550 switch (message) { 551 case WM_INITDIALOG: 552#if CYGDEBUG 553 winDebug("winAboutDlgProc - WM_INITDIALOG\n"); 554#endif 555 556 /* Store pointer to private structure for future use */ 557 s_pScreenPriv = (winPrivScreenPtr) lParam; 558 559 winInitDialog(hwndDialog); 560 561 /* Override the URL buttons */ 562 winOverrideURLButton(hwndDialog, ID_ABOUT_WEBSITE); 563 564 return TRUE; 565 566 case WM_DRAWITEM: 567 /* Draw the URL buttons as needed */ 568 winDrawURLWindow(lParam); 569 return TRUE; 570 571 case WM_MOUSEMOVE: 572 case WM_NCMOUSEMOVE: 573 /* Show the cursor if it is hidden */ 574 if (g_fSoftwareCursor && !g_fCursor) { 575 g_fCursor = TRUE; 576 ShowCursor(TRUE); 577 } 578 return TRUE; 579 580 case WM_COMMAND: 581 switch (LOWORD(wParam)) { 582 case IDOK: 583 case IDCANCEL: 584 winDebug("winAboutDlgProc - WM_COMMAND - IDOK or IDCANCEL\n"); 585 586 DestroyWindow(g_hDlgAbout); 587 g_hDlgAbout = NULL; 588 589 /* Fix to make sure keyboard focus isn't trapped */ 590 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 591 592 /* Restore window procedures for URL buttons */ 593 winUnoverrideURLButton(hwndDialog, ID_ABOUT_WEBSITE); 594 595 return TRUE; 596 597 case ID_ABOUT_WEBSITE: 598 { 599 const char *pszPath = __VENDORDWEBSUPPORT__; 600 INT_PTR iReturn; 601 602 iReturn = (INT_PTR) ShellExecute(NULL, 603 "open", 604 pszPath, NULL, NULL, SW_MAXIMIZE); 605 if (iReturn < 32) { 606 ErrorF("winAboutDlgProc - WM_COMMAND - ID_ABOUT_WEBSITE - " 607 "ShellExecute failed: %d\n", (int)iReturn); 608 609 } 610 } 611 return TRUE; 612 } 613 break; 614 615 case WM_CLOSE: 616 winDebug("winAboutDlgProc - WM_CLOSE\n"); 617 618 DestroyWindow(g_hDlgAbout); 619 g_hDlgAbout = NULL; 620 621 /* Fix to make sure keyboard focus isn't trapped */ 622 PostMessage(s_pScreenPriv->hwndScreen, WM_NULL, 0, 0); 623 624 /* Restore window procedures for URL buttons */ 625 winUnoverrideURLButton(hwndDialog, ID_ABOUT_WEBSITE); 626 627 return TRUE; 628 } 629 630 return FALSE; 631} 632