/ source / blood / src / startwin.game.cpp
startwin.game.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2010-2019 EDuke32 developers and contributors
  4  Copyright (C) 2019 Nuke.YKT
  5  
  6  This file is part of NBlood.
  7  
  8  NBlood is free software; you can redistribute it and/or
  9  modify it under the terms of the GNU General Public License version 2
 10  as published by the Free Software Foundation.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  */
 22  //-------------------------------------------------------------------------
 23  
 24  #ifndef _WIN32
 25  #error Only for Windows
 26  #endif
 27  
 28  #include "renderlayer.h"
 29  
 30  #ifdef STARTUP_SETUP_WINDOW
 31  
 32  #define NEED_WINDOWSX_H
 33  #define NEED_COMMCTRL_H
 34  #define ONLY_USERDEFS
 35  
 36  #include "_control.h"
 37  #include "build.h"
 38  #include "cache1d.h"
 39  //#include "cmdline.h"
 40  #include "common_game.h"
 41  #include "compat.h"
 42  #include "control.h"
 43  #include "config.h"
 44  //#include "function.h"
 45  //#include "game.h"
 46  //#include "grpscan.h"
 47  //#include "inv.h"
 48  #include "blood.h"
 49  #include "globals.h"
 50  #include "keyboard.h"
 51  #include "startwin.game.h"
 52  #include "windows_inc.h"
 53  
 54  #define TAB_CONFIG 0
 55  #define TAB_MESSAGES 1
 56  
 57  static struct
 58  {
 59      INICHAIN const * ini;
 60      char *gamedir;
 61      ud_setup_t shared;
 62      int polymer;
 63  }
 64  settings;
 65  
 66  static HWND startupdlg;
 67  static HWND pages[3];
 68  static int done = -1;
 69  static int mode = TAB_CONFIG;
 70  
 71  static BUILDVFS_FIND_REC *finddirs;
 72  
 73  static inline void clearfilenames(void)
 74  {
 75      klistfree(finddirs);
 76      finddirs = NULL;
 77  }
 78  
 79  static inline void getfilenames(char const *path)
 80  {
 81      clearfilenames();
 82      finddirs = klistpath(path,"*",BUILDVFS_FIND_DIR);
 83  }
 84  
 85  #define POPULATE_VIDEO 1
 86  #define POPULATE_CONFIG 2
 87  #define POPULATE_GAME 4
 88  #define POPULATE_GAMEDIRS 8
 89  
 90  #ifdef INPUT_MOUSE
 91  #undef INPUT_MOUSE
 92  #endif
 93  
 94  #define INPUT_KB 0
 95  #define INPUT_MOUSE 1
 96  #define INPUT_JOYSTICK 2
 97  #define INPUT_ALL 3
 98  
 99  const char *controlstrings[] = { "Keyboard only", "Keyboard and mouse", "Keyboard and joystick", "All supported devices" };
100  
101  static void PopulateForm(int32_t pgs)
102  {
103      char buf[512];
104  
105      if (pgs & POPULATE_GAMEDIRS)
106      {
107          HWND hwnd = GetDlgItem(pages[TAB_CONFIG], IDCGAMEDIR);
108  
109          getfilenames("/");
110          (void)ComboBox_ResetContent(hwnd);
111          int const r = ComboBox_AddString(hwnd, "None");
112          (void)ComboBox_SetItemData(hwnd, r, 0);
113          (void)ComboBox_SetCurSel(hwnd, r);
114          auto dirs = finddirs;
115          for (int i=1, j=1; dirs != NULL; dirs=dirs->next)
116          {
117              if (Bstrcasecmp(dirs->name, "autoload") == 0)
118              {
119                  j++;
120                  continue;
121              }
122  
123              (void)ComboBox_AddString(hwnd, dirs->name);
124              (void)ComboBox_SetItemData(hwnd, i, j);
125              if (Bstrcasecmp(dirs->name, settings.gamedir) == 0)
126                  (void)ComboBox_SetCurSel(hwnd, i);
127  
128              i++;
129              j++;
130          }
131      }
132  
133      if (pgs & POPULATE_VIDEO)
134      {
135          HWND hwnd = GetDlgItem(pages[TAB_CONFIG], IDCVMODE);
136          int mode = videoCheckMode(&settings.shared.xdim, &settings.shared.ydim, settings.shared.bpp, settings.shared.fullscreen, 1);
137  
138          if (mode < 0 || (settings.shared.bpp < 15 && (settings.polymer)))
139          {
140              int CONSTEXPR cd[] = { 32, 24, 16, 15, 8, 0 };
141              int i;
142  
143              for (i=0; cd[i];)
144              {
145                  if (cd[i] >= settings.shared.bpp) i++;
146                  else break;
147              }
148              for (; cd[i]; i++)
149              {
150                  mode = videoCheckMode(&settings.shared.xdim, &settings.shared.ydim, cd[i], settings.shared.fullscreen, 1);
151                  if (mode < 0) continue;
152                  settings.shared.bpp = cd[i];
153                  break;
154              }
155          }
156  
157          Button_SetCheck(GetDlgItem(pages[TAB_CONFIG], IDCFULLSCREEN), ((settings.shared.fullscreen) ? BST_CHECKED : BST_UNCHECKED));
158          //Button_SetCheck(GetDlgItem(pages[TAB_CONFIG], IDCPOLYMER), ((settings.polymer) ? BST_CHECKED : BST_UNCHECKED));
159  
160          (void)ComboBox_ResetContent(hwnd);
161  
162          for (int i=0; i<validmodecnt; i++)
163          {
164              if (validmode[i].fs != (settings.shared.fullscreen)) continue;
165              if ((validmode[i].bpp < 15) && (settings.polymer)) continue;
166  
167              // all modes get added to the 3D mode list
168              Bsprintf(buf, "%dx%d %s", validmode[i].xdim, validmode[i].ydim, validmode[i].bpp == 8 ? "software" : "OpenGL");
169              int const j = ComboBox_AddString(hwnd, buf);
170              (void)ComboBox_SetItemData(hwnd, j, i);
171              if (i == mode)(void)ComboBox_SetCurSel(hwnd, j);
172          }
173      }
174  
175      if (pgs & POPULATE_CONFIG)
176      {
177          Button_SetCheck(GetDlgItem(pages[TAB_CONFIG], IDCALWAYSSHOW), (settings.shared.forcesetup ? BST_CHECKED : BST_UNCHECKED));
178          Button_SetCheck(GetDlgItem(pages[TAB_CONFIG], IDCAUTOLOAD), (!(settings.shared.noautoload) ? BST_CHECKED : BST_UNCHECKED));
179          Button_SetCheck(GetDlgItem(pages[TAB_CONFIG], IDCQUICKSTART), ((settings.shared.quickstart) ? BST_CHECKED : BST_UNCHECKED));
180  
181          HWND hwnd = GetDlgItem(pages[TAB_CONFIG], IDCINPUT);
182  
183          (void)ComboBox_ResetContent(hwnd);
184          (void)ComboBox_SetCurSel(hwnd, 0);
185  
186          int j = 4;
187  
188          for (int i=0; i<j; i++)
189          {
190              (void)ComboBox_InsertString(hwnd, i, controlstrings[i]);
191              (void)ComboBox_SetItemData(hwnd, i, i);
192  
193              switch (i)
194              {
195              case INPUT_MOUSE:
196                  if (settings.shared.usemouse && !settings.shared.usejoystick)(void)ComboBox_SetCurSel(hwnd, i);
197                  break;
198              case INPUT_JOYSTICK:
199                  if (!settings.shared.usemouse && settings.shared.usejoystick)(void)ComboBox_SetCurSel(hwnd, i);
200                  break;
201              case INPUT_ALL:
202                  if (settings.shared.usemouse && settings.shared.usejoystick)(void)ComboBox_SetCurSel(hwnd, i);
203                  break;
204              }
205          }
206      }
207  
208      if (pgs & POPULATE_GAME)
209      {
210          HWND hwnd = GetDlgItem(pages[TAB_CONFIG], IDCDATA);
211  
212          for (auto fg = pINIChain; fg; fg=fg->pNext)
213          {
214              if (fg->pDescription)
215                  Bsprintf(buf, "%s\t%s", fg->pDescription->pzName, fg->zName);
216              else
217                  Bsprintf(buf, "%s", fg->zName);
218              int const j = ListBox_AddString(hwnd, buf);
219              (void)ListBox_SetItemData(hwnd, j, (LPARAM)fg);
220              if (settings.ini == fg)
221                  (void)ListBox_SetCurSel(hwnd, j);
222          }
223      }
224  }
225  
226  static INT_PTR CALLBACK ConfigPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
227  {
228      switch (uMsg)
229      {
230      case WM_COMMAND:
231          switch (LOWORD(wParam))
232          {
233          case IDCFULLSCREEN:
234              settings.shared.fullscreen = !settings.shared.fullscreen;
235              PopulateForm(POPULATE_VIDEO);
236              return TRUE;
237          //case IDCPOLYMER:
238          //    settings.polymer = !settings.polymer;
239          //    if (settings.shared.bpp == 8) settings.shared.bpp = 32;
240          //    PopulateForm(POPULATE_VIDEO);
241          //    return TRUE;
242          case IDCVMODE:
243              if (HIWORD(wParam) == CBN_SELCHANGE)
244              {
245                  int i = ComboBox_GetCurSel((HWND)lParam);
246                  if (i != CB_ERR) i = ComboBox_GetItemData((HWND)lParam, i);
247                  if (i != CB_ERR)
248                  {
249                      settings.shared.xdim = validmode[i].xdim;
250                      settings.shared.ydim = validmode[i].ydim;
251                      settings.shared.bpp  = validmode[i].bpp;
252                  }
253              }
254              return TRUE;
255          case IDCALWAYSSHOW:
256              settings.shared.forcesetup = IsDlgButtonChecked(hwndDlg, IDCALWAYSSHOW) == BST_CHECKED;
257              return TRUE;
258          case IDCAUTOLOAD:
259              settings.shared.noautoload = (IsDlgButtonChecked(hwndDlg, IDCAUTOLOAD) != BST_CHECKED);
260              return TRUE;
261          case IDCQUICKSTART:
262              settings.shared.quickstart = (IsDlgButtonChecked(hwndDlg, IDCQUICKSTART) == BST_CHECKED);
263              return TRUE;
264          case IDCINPUT:
265              if (HIWORD(wParam) == CBN_SELCHANGE)
266              {
267                  int i = ComboBox_GetCurSel((HWND)lParam);
268                  if (i != CB_ERR) i = ComboBox_GetItemData((HWND)lParam, i);
269                  if (i != CB_ERR)
270                  {
271                      switch (i)
272                      {
273                      case INPUT_KB:
274                          settings.shared.usemouse = settings.shared.usejoystick = 0;
275                          break;
276                      case INPUT_MOUSE:
277                          settings.shared.usemouse = 1;
278                          settings.shared.usejoystick = 0;
279                          break;
280                      case INPUT_JOYSTICK:
281                          settings.shared.usemouse = 0;
282                          settings.shared.usejoystick = 1;
283                          break;
284                      case INPUT_ALL:
285                          settings.shared.usemouse = settings.shared.usejoystick = 1;
286                          break;
287                      }
288                  }
289              }
290              return TRUE;
291  
292          case IDCGAMEDIR:
293              if (HIWORD(wParam) == CBN_SELCHANGE)
294              {
295                  int i = ComboBox_GetCurSel((HWND)lParam);
296                  if (i != CB_ERR) i = ComboBox_GetItemData((HWND)lParam, i);
297                  if (i != CB_ERR)
298                  {
299                      if (i==0)
300                          settings.gamedir = NULL;
301                      else
302                      {
303                          BUILDVFS_FIND_REC *dir = finddirs;
304                          for (int j = 1; dir != NULL; dir = dir->next, j++)
305                          {
306                              if (j == i)
307                              {
308                                  settings.gamedir = dir->name;
309                                  break;
310                              }
311                          }
312                      }
313                  }
314              }
315              return TRUE;
316          case IDCDATA:
317          {
318              if (HIWORD(wParam) != LBN_SELCHANGE) break;
319              intptr_t i = ListBox_GetCurSel((HWND)lParam);
320              if (i != CB_ERR) i = ListBox_GetItemData((HWND)lParam, i);
321              if (i != CB_ERR)
322              {
323                  settings.ini = (INICHAIN const *)i;
324  
325                  char szPickedIni[BMAX_PATH] = "";
326                  for (i = 0; (settings.ini->zName[i] != '.') && (settings.ini->zName[i] != '\0'); i++)
327                      szPickedIni[i] = settings.ini->zName[i];
328                  if (szPickedIni[0] != '\0')
329                  {
330                      HWND hwnd = GetDlgItem(pages[TAB_CONFIG], IDCGAMEDIR);
331                      i = ComboBox_SelectString(hwnd, 0, (LPSTR)szPickedIni);
332                      if (i == CB_ERR) // could not find potential mod folder, reset back to none
333                      {
334                          settings.gamedir = NULL;
335                          (void)ComboBox_SetCurSel(hwnd, 0);
336                      }
337                      else
338                      {
339                          i = ComboBox_GetItemData(hwnd, i);
340                          BUILDVFS_FIND_REC *dir = finddirs;
341                          for (int j = 1; dir != NULL; dir = dir->next, j++)
342                          {
343                              if (j == i)
344                              {
345                                  settings.gamedir = dir->name;
346                                  break;
347                              }
348                          }
349                      }
350                  }
351              }
352              return TRUE;
353          }
354          default:
355              break;
356          }
357          break;
358      default:
359          break;
360      }
361      return FALSE;
362  }
363  
364  
365  static void SetPage(int pageNum)
366  {
367      HWND tab = GetDlgItem(startupdlg, WIN_STARTWIN_TABCTL);
368      auto const cur = SendMessage(tab, TCM_GETCURSEL, 0, 0);
369      ShowWindow(pages[cur], SW_HIDE);
370      SendMessage(tab, TCM_SETCURSEL, pageNum, 0);
371      ShowWindow(pages[pageNum], SW_SHOW);
372      mode = pageNum;
373  
374      SetFocus(GetDlgItem(startupdlg, WIN_STARTWIN_TABCTL));
375  }
376  
377  static void EnableConfig(bool n)
378  {
379  #ifndef NETCODE_DISABLE
380      EnableWindow(GetDlgItem(startupdlg, WIN_STARTWIN_UPDATE), n);
381  #else
382      ShowWindow(GetDlgItem(startupdlg, WIN_STARTWIN_UPDATE), SW_HIDE);
383  #endif
384      //EnableWindow(GetDlgItem(startupdlg, WIN_STARTWIN_CANCEL), n);
385      EnableWindow(GetDlgItem(startupdlg, WIN_STARTWIN_START), n);
386      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCDATA), n);
387      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCFULLSCREEN), n);
388      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCGAMEDIR), n);
389      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCINPUT), n);
390      //EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCPOLYMER), n);
391      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCQUICKSTART), n);
392      EnableWindow(GetDlgItem(pages[TAB_CONFIG], IDCVMODE), n);
393  }
394  
395  #ifndef NETCODE_DISABLE
396  bool ExecUpdater(void)
397  {
398      STARTUPINFO si;
399      PROCESS_INFORMATION pi;
400      char szRootDir[BMAX_PATH], szUpdaterPath[BMAX_PATH];
401  
402      ZeroMemory(&si,sizeof(si));
403      ZeroMemory(&pi,sizeof(pi));
404      si.cb = sizeof(si);
405      si.dwFlags = STARTF_USESHOWWINDOW;
406      si.wShowWindow = SW_HIDE;
407  
408      GetModuleFileName(NULL,szRootDir,BMAX_PATH);
409      Bcorrectfilename(szRootDir,1);
410      Bstrcpy(szUpdaterPath, szRootDir);
411      Bstrcat(szUpdaterPath,"notbloodupdate.exe -RestartIfSuccessful");
412  
413      if (!CreateProcessA(nullptr,(LPSTR)szUpdaterPath,nullptr,nullptr,FALSE,CREATE_NO_WINDOW,nullptr,szRootDir,&si,&pi))
414      {
415          MessageBox(win_gethwnd(),"Error, could not launch notbloodupdate.exe\n\nPlease reinstall your copy of NotBlood.","Error",MB_OK|MB_ICONERROR);
416          return false;
417      }
418      return true;
419  }
420  #endif
421  
422  static INT_PTR CALLBACK startup_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
423  {
424      static HBITMAP hbmp = NULL;
425  
426      switch (uMsg)
427      {
428      case WM_INITDIALOG:
429      {
430          // Fetch the positions (in screen coordinates) of all the windows we need to tweak
431          RECT chrome = {};
432          AdjustWindowRect(&chrome, GetWindowLong(hwndDlg, GWL_STYLE), FALSE);
433          RECT rdlg;
434          GetWindowRect(hwndDlg, &rdlg);
435  
436          // Knock off the non-client area of the main dialogue to give just the client area
437          rdlg.left -= chrome.left;
438          rdlg.top -= chrome.top;
439          rdlg.right -= chrome.right;
440          rdlg.bottom -= chrome.bottom;
441  
442          RECT rtab;
443          GetWindowRect(GetDlgItem(hwndDlg, WIN_STARTWIN_TABCTL), &rtab);
444  
445          // Translate them to client-relative coordinates wrt the main dialogue window
446          rtab.right -= rtab.left - 1;
447          rtab.bottom -= rtab.top - 1;
448          rtab.left  -= rdlg.left;
449          rtab.top -= rdlg.top;
450  
451          RECT rupdate;
452          GetWindowRect(GetDlgItem(hwndDlg, WIN_STARTWIN_UPDATE), &rupdate);
453  
454          rupdate.right -= rupdate.left - 1;
455          rupdate.bottom -= rupdate.top - 1;
456          rupdate.left -= rdlg.left;
457          rupdate.top -= rdlg.top;
458  
459          RECT rcancel;
460          GetWindowRect(GetDlgItem(hwndDlg, WIN_STARTWIN_CANCEL), &rcancel);
461  
462          rcancel.right -= rcancel.left - 1;
463          rcancel.bottom -= rcancel.top - 1;
464          rcancel.left -= rdlg.left;
465          rcancel.top -= rdlg.top;
466  
467          RECT rstart;
468          GetWindowRect(GetDlgItem(hwndDlg, WIN_STARTWIN_START), &rstart);
469  
470          rstart.right -= rstart.left - 1;
471          rstart.bottom -= rstart.top - 1;
472          rstart.left -= rdlg.left;
473          rstart.top -= rdlg.top;
474  
475          // And then convert the main dialogue coordinates to just width/length
476          rdlg.right -= rdlg.left - 1;
477          rdlg.bottom -= rdlg.top - 1;
478          rdlg.left = 0;
479          rdlg.top = 0;
480  
481          // Load the bitmap into the bitmap control and fetch its dimensions
482          hbmp = LoadBitmap((HINSTANCE)win_gethinstance(), MAKEINTRESOURCE(RSRC_BMP));
483  
484          HWND hwnd = GetDlgItem(hwndDlg, WIN_STARTWIN_BITMAP);
485          SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp);
486  
487          RECT r;
488          GetClientRect(hwnd, &r);
489  
490          int const xoffset = r.right;
491          int const yoffset = r.bottom - rdlg.bottom;
492  
493          // Shift and resize the controls that require it
494          rtab.left += xoffset;
495          rtab.bottom += yoffset;
496          rupdate.left = r.right-r.left+7;
497          rupdate.top += yoffset;
498          rcancel.left += xoffset;
499          rcancel.top += yoffset;
500          rstart.left += xoffset;
501          rstart.top += yoffset;
502          rdlg.right += xoffset;
503          rdlg.bottom += yoffset;
504  
505          // Move the controls to their new positions
506          MoveWindow(GetDlgItem(hwndDlg, WIN_STARTWIN_TABCTL), rtab.left, rtab.top, rtab.right, rtab.bottom, FALSE);
507          MoveWindow(GetDlgItem(hwndDlg, WIN_STARTWIN_UPDATE), rupdate.left, rupdate.top, rupdate.right, rupdate.bottom, FALSE);
508          MoveWindow(GetDlgItem(hwndDlg, WIN_STARTWIN_CANCEL), rcancel.left, rcancel.top, rcancel.right, rcancel.bottom, FALSE);
509          MoveWindow(GetDlgItem(hwndDlg, WIN_STARTWIN_START), rstart.left, rstart.top, rstart.right, rstart.bottom, FALSE);
510  
511          // Move the main dialogue to the centre of the screen
512          HDC hdc = GetDC(NULL);
513          rdlg.left = (GetDeviceCaps(hdc, HORZRES) - rdlg.right) / 2;
514          rdlg.top = (GetDeviceCaps(hdc, VERTRES) - rdlg.bottom) / 2;
515          ReleaseDC(NULL, hdc);
516          MoveWindow(hwndDlg, rdlg.left + chrome.left, rdlg.top + chrome.left,
517                     rdlg.right + (-chrome.left+chrome.right), rdlg.bottom + (-chrome.top+chrome.bottom), TRUE);
518  
519          // Add tabs to the tab control
520          {
521              static char textSetup[] = TEXT("Setup");
522              static char textMessageLog[] = TEXT("Message Log");
523  
524              hwnd = GetDlgItem(hwndDlg, WIN_STARTWIN_TABCTL);
525  
526              TCITEM tab = {};
527              tab.mask = TCIF_TEXT;
528              tab.pszText = textSetup;
529              SendMessage(hwnd, TCM_INSERTITEM, (WPARAM)TAB_CONFIG, (LPARAM)&tab);
530              tab.mask = TCIF_TEXT;
531              tab.pszText = textMessageLog;
532              SendMessage(hwnd, TCM_INSERTITEM, (WPARAM)TAB_MESSAGES, (LPARAM)&tab);
533  
534              // Work out the position and size of the area inside the tab control for the pages
535              ZeroMemory(&r, sizeof(r));
536              GetClientRect(hwnd, &r);
537              SendMessage(hwnd, TCM_ADJUSTRECT, FALSE, (LPARAM)&r);
538              r.right -= r.left-1;
539              r.bottom -= r.top-1;
540              r.top += rtab.top;
541              r.left += rtab.left;
542  
543              // Create the pages and position them in the tab control, but hide them
544              pages[TAB_CONFIG] = CreateDialog((HINSTANCE)win_gethinstance(), MAKEINTRESOURCE(WIN_STARTWINPAGE_CONFIG), hwndDlg, ConfigPageProc);
545              SetWindowPos(pages[TAB_CONFIG], hwnd, r.left, r.top, r.right, r.bottom, SWP_HIDEWINDOW);
546  
547              pages[TAB_MESSAGES] = GetDlgItem(hwndDlg, WIN_STARTWIN_MESSAGES);
548              SetWindowPos(pages[TAB_MESSAGES], hwnd, r.left, r.top, r.right, r.bottom, SWP_HIDEWINDOW);
549  
550              // Tell the editfield acting as the console to exclude the width of the scrollbar
551              GetClientRect(pages[TAB_MESSAGES], &r);
552              r.right -= GetSystemMetrics(SM_CXVSCROLL)+4;
553              r.left = r.top = 0;
554              SendMessage(pages[TAB_MESSAGES], EM_SETRECTNP,0,(LPARAM)&r);
555  
556              // Set a tab stop in the game data listbox
557              {
558                  DWORD tabs[1] = { 150 };
559                  (void)ListBox_SetTabStops(GetDlgItem(pages[TAB_CONFIG], IDCDATA), 1, tabs);
560              }
561  
562              SetFocus(GetDlgItem(hwndDlg, WIN_STARTWIN_START));
563              SetWindowText(hwndDlg, apptitle);
564          }
565          return FALSE;
566      }
567  
568      case WM_NOTIFY:
569      {
570          auto nmhdr = (LPNMHDR)lParam;
571          if (nmhdr->idFrom != WIN_STARTWIN_TABCTL) break;
572          int const cur = SendMessage(nmhdr->hwndFrom, TCM_GETCURSEL,0,0);
573          switch (nmhdr->code)
574          {
575              case TCN_SELCHANGING:
576              case TCN_SELCHANGE:
577                  if (cur < 0 || !pages[cur])
578                      break;
579                  ShowWindow(pages[cur], nmhdr->code == TCN_SELCHANGING ? SW_HIDE : SW_SHOW);
580                  return TRUE;
581          }
582          break;
583      }
584  
585      case WM_CLOSE:
586          if (mode == TAB_CONFIG) done = 0;
587          else quitevent++;
588          return TRUE;
589  
590      case WM_DESTROY:
591          if (hbmp)
592          {
593              DeleteObject(hbmp);
594              hbmp = NULL;
595          }
596  
597          if (pages[TAB_CONFIG])
598          {
599              DestroyWindow(pages[TAB_CONFIG]);
600              pages[TAB_CONFIG] = NULL;
601          }
602  
603          startupdlg = NULL;
604          return TRUE;
605  
606      case WM_COMMAND:
607          switch (LOWORD(wParam))
608          {
609          case WIN_STARTWIN_UPDATE:
610  #ifndef NETCODE_DISABLE
611              if (!ExecUpdater()) break;
612              done = 0;
613              return TRUE;
614  #else
615              break;
616  #endif
617          case WIN_STARTWIN_CANCEL:
618              if (mode == TAB_CONFIG) done = 0;
619              else quitevent++;
620              return TRUE;
621          case WIN_STARTWIN_START:
622              done = 1;
623              return TRUE;
624          }
625          return FALSE;
626  
627      case WM_CTLCOLORSTATIC:
628          if ((HWND)lParam == pages[TAB_MESSAGES])
629              return (BOOL)(intptr_t)GetSysColorBrush(COLOR_WINDOW);
630          break;
631  
632      default:
633          break;
634      }
635  
636      return FALSE;
637  }
638  
639  bool startwin_isopen(void)
640  {
641      return !!startupdlg;
642  }
643  
644  int32_t startwin_open(void)
645  {
646      if (startupdlg) return 1;
647      INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_TAB_CLASSES };
648      InitCommonControlsEx(&icc);
649      startupdlg = CreateDialog((HINSTANCE)win_gethinstance(), MAKEINTRESOURCE(WIN_STARTWIN), NULL, startup_dlgproc);
650      if (startupdlg)
651      {
652          SetPage(TAB_MESSAGES);
653          EnableConfig(0);
654          return 0;
655      }
656      return -1;
657  }
658  
659  int32_t startwin_close(void)
660  {
661      if (!startupdlg) return 1;
662      DestroyWindow(startupdlg);
663      startupdlg = NULL;
664      return 0;
665  }
666  
667  int32_t startwin_puts(const char *buf)
668  {
669      if (!startupdlg) return 1;
670  
671      const HWND edctl = pages[TAB_MESSAGES];
672  
673      if (!edctl) return -1;
674  
675      static HWND dactrl = NULL;
676      if (!dactrl) dactrl = GetDlgItem(startupdlg, WIN_STARTWIN_TABCTL);
677  
678      int const vis = ((int)SendMessage(dactrl, TCM_GETCURSEL,0,0) == TAB_MESSAGES);
679  
680      if (vis)
681          SendMessage(edctl, WM_SETREDRAW, FALSE, 0);
682  
683      int const curlen = SendMessage(edctl, WM_GETTEXTLENGTH, 0,0);
684      SendMessage(edctl, EM_SETSEL, (WPARAM)curlen, (LPARAM)curlen);
685  
686      int const   numlines = SendMessage(edctl, EM_GETLINECOUNT, 0, 0);
687      static bool newline  = false;
688      const char *p        = buf;
689  
690      while (*p)
691      {
692          if (newline)
693          {
694              SendMessage(edctl, EM_REPLACESEL, 0, (LPARAM)"\r\n");
695              newline = false;
696          }
697          const char *q = p;
698          while (*q && *q != '\n') q++;
699          static char workbuf[1024];
700          Bmemcpy(workbuf, p, q-p);
701          if (*q == '\n')
702          {
703              if (!q[1])
704              {
705                  newline = true;
706                  workbuf[q-p] = 0;
707              }
708              else
709              {
710                  workbuf[q-p] = '\r';
711                  workbuf[q-p+1] = '\n';
712                  workbuf[q-p+2] = 0;
713              }
714              p = q+1;
715          }
716          else
717          {
718              workbuf[q-p] = 0;
719              p = q;
720          }
721          SendMessage(edctl, EM_REPLACESEL, 0, (LPARAM)workbuf);
722      }
723  
724      int const newnumlines = SendMessage(edctl, EM_GETLINECOUNT, 0, 0);
725      SendMessage(edctl, EM_LINESCROLL, 0, newnumlines - numlines);
726  
727      if (vis)
728          SendMessage(edctl, WM_SETREDRAW, TRUE, 0);
729  
730      return 0;
731  }
732  
733  int32_t startwin_settitle(const char *str)
734  {
735      if (!startupdlg) return 1;
736      SetWindowText(startupdlg, str);
737      return 0;
738  }
739  
740  int32_t startwin_idle(void *v)
741  {
742      if (!startupdlg || !IsWindow(startupdlg)) return 0;
743      if (IsDialogMessage(startupdlg, (MSG *)v)) return 1;
744      return 0;
745  }
746  
747  int32_t startwin_run(void)
748  {
749      if (!startupdlg) return 1;
750  
751      done = -1;
752  
753      SetPage(TAB_CONFIG);
754      EnableConfig(1);
755  
756  #ifdef POLYMER
757      settings.polymer = (glrendmode == REND_POLYMER);
758  #else
759      settings.polymer = 0;
760  #endif
761  
762      settings.shared = gSetup;
763      settings.ini = pINISelected;
764      settings.gamedir = g_modDir;
765  
766      PopulateForm(-1);
767  
768      do
769      {
770          MSG msg;
771  
772          switch (GetMessage(&msg, NULL, 0,0))
773          {
774          case 0:
775              done = 1;
776              break;
777          case -1:
778              return -1;
779          default:
780              if (IsWindow(startupdlg) && IsDialogMessage(startupdlg, &msg))
781                  break;
782              TranslateMessage(&msg);
783              DispatchMessage(&msg);
784              break;
785          }
786      }
787      while (done < 0);
788  
789      SetPage(TAB_MESSAGES);
790      EnableConfig(0);
791  
792      if (done)
793      {
794          gSetup = settings.shared;
795  #ifdef USE_OPENGL
796          glrendmode = (settings.polymer) ? REND_POLYMER : REND_POLYMOST;
797  #endif
798          pINISelected = settings.ini;
799          if (g_modDir != settings.gamedir)
800              Bstrcpy(g_modDir, (gNoSetup == 0 && settings.gamedir != NULL) ? settings.gamedir : "/");
801      }
802  
803      return done;
804  }
805  
806  #endif // STARTUP_SETUP_WINDOW