hpcmenu.cpp revision 1.4 1 /* -*-C++-*- $NetBSD: hpcmenu.cpp,v 1.4 2001/03/22 18:26:45 uch Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <hpcmenu.h>
40 #include <hpcboot.h>
41 #include <res/resource.h>
42 #include <menu/window.h>
43 #include <menu/tabwindow.h>
44 #include <menu/rootwindow.h>
45 #include <machine/bootinfo.h>
46 #include <framebuffer.h>
47 #include <console.h>
48
49 static BOOL _find_pref_dir(TCHAR *);
50 //
51 // Main window
52 //
53 class MainTabWindow : public TabWindow
54 {
55 private:
56 HWND _edit_md_root;
57
58 int _item_idx;
59 void _insert_item(HWND w, TCHAR *name, int id) {
60 int idx = SendDlgItemMessage(w, id, CB_ADDSTRING, 0,
61 reinterpret_cast <LPARAM>(name));
62 if (idx != CB_ERR)
63 SendDlgItemMessage(w, IDC_MAIN_DIR, CB_SETITEMDATA,
64 idx, _item_idx++);
65 }
66
67 public:
68 explicit MainTabWindow(TabWindowBase &base, int id)
69 : TabWindow(base, id, TEXT("WMain")) {
70 _item_idx = 0;
71 }
72 virtual ~MainTabWindow(void) { /* NO-OP */ }
73 virtual void init(HWND w) {
74 HpcMenuInterface &menu = HpcMenuInterface::Instance();
75 struct HpcMenuInterface::HpcMenuPreferences
76 *pref = &menu._pref;
77 _window = w;
78 // insert myself to tab-control
79 TabWindow::init(w);
80
81 // setup child.
82 TCHAR *entry;
83 int i;
84 // kernel directory path
85 for (i = 0; entry = menu.dir(i); i++)
86 _insert_item(w, entry, IDC_MAIN_DIR);
87 SendDlgItemMessage(w, IDC_MAIN_DIR, CB_SETCURSEL,
88 menu.dir_default(), 0);
89 // platform
90 for (i = 0; entry = menu.platform_get(i); i++)
91 _insert_item(w, entry, IDC_MAIN_PLATFORM);
92 SendDlgItemMessage(w, IDC_MAIN_PLATFORM, CB_SETCURSEL,
93 menu.platform_default(), 0);
94 // kernel file name.
95 Edit_SetText(GetDlgItem(w, IDC_MAIN_KERNEL),
96 pref->kernel_user ? pref->kernel_user_file :
97 TEXT("netbsd.gz"));
98
99 // root file system.
100 int fs = pref->rootfs + IDC_MAIN_ROOT_;
101 _set_check(fs, TRUE);
102
103 _edit_md_root = GetDlgItem(w, IDC_MAIN_ROOT_MD_OPS);
104 Edit_SetText(_edit_md_root, pref->rootfs_file);
105 EnableWindow(_edit_md_root, fs == IDC_MAIN_ROOT_MD
106 ? TRUE : FALSE);
107
108 // kernel boot options.
109 _set_check(IDC_MAIN_OPTION_A, pref->boot_ask_for_name);
110 _set_check(IDC_MAIN_OPTION_S, pref->boot_single_user);
111 _set_check(IDC_MAIN_OPTION_V, pref->boot_verbose);
112 _set_check(IDC_MAIN_OPTION_H, pref->boot_serial);
113 }
114
115 void get(void) {
116 HpcMenuInterface &menu = HpcMenuInterface::Instance();
117 struct HpcMenuInterface::HpcMenuPreferences
118 *pref = &menu._pref;
119
120 HWND w = GetDlgItem(_window, IDC_MAIN_DIR);
121 ComboBox_GetText(w, pref->dir_user_path, MAX_PATH);
122 pref->dir_user = TRUE;
123 w = GetDlgItem(_window, IDC_MAIN_KERNEL);
124 Edit_GetText(w, pref->kernel_user_file, MAX_PATH);
125 pref->kernel_user = TRUE;
126
127 int i = ComboBox_GetCurSel(GetDlgItem(_window,
128 IDC_MAIN_PLATFORM));
129 menu.platform_set(i);
130
131 if (_is_checked(IDC_MAIN_ROOT_WD))
132 pref->rootfs = 0;
133 else if (_is_checked(IDC_MAIN_ROOT_SD))
134 pref->rootfs = 1;
135 else if (_is_checked(IDC_MAIN_ROOT_MD))
136 pref->rootfs = 2;
137 else if (_is_checked(IDC_MAIN_ROOT_NFS))
138 pref->rootfs = 3;
139
140 pref->boot_ask_for_name = _is_checked(IDC_MAIN_OPTION_A);
141 pref->boot_verbose = _is_checked(IDC_MAIN_OPTION_V);
142 pref->boot_single_user = _is_checked(IDC_MAIN_OPTION_S);
143 pref->boot_serial = _is_checked(IDC_MAIN_OPTION_H);
144 Edit_GetText(_edit_md_root, pref->rootfs_file, MAX_PATH);
145 }
146
147 virtual void command(int id, int msg) {
148 EnableWindow(_edit_md_root,
149 _is_checked(IDC_MAIN_ROOT_MD) ? TRUE : FALSE);
150 }
151 };
152
153 //
154 // Option window
155 //
156 class OptionTabWindow : public TabWindow
157 {
158 public:
159 HWND _spin_edit;
160 HWND _spin;
161 #define IS_CHECKED(x) _is_checked(IDC_OPT_##x)
162 #define SET_CHECK(x, b) _set_check(IDC_OPT_##x,(b))
163
164 public:
165 explicit OptionTabWindow(TabWindowBase &base, int id)
166 : TabWindow(base, id, TEXT("WOption")) {
167 _spin_edit = NULL;
168 _spin = NULL;
169 }
170 virtual ~OptionTabWindow(void) { /* NO-OP */ }
171 virtual void init(HWND w) {
172 HpcMenuInterface &menu = HpcMenuInterface::Instance();
173 struct HpcMenuInterface::HpcMenuPreferences
174 *pref = &menu._pref;
175 _window = w;
176
177 TabWindow::init(_window);
178 _spin_edit = GetDlgItem(_window, IDC_OPT_AUTO_INPUT);
179 _spin = CreateUpDownControl(WS_CHILD | WS_BORDER | WS_VISIBLE |
180 UDS_SETBUDDYINT | UDS_ALIGNRIGHT,
181 80, 0, 50, 50, _window,
182 IDC_OPT_AUTO_UPDOWN,
183 _app._instance, _spin_edit,
184 60, 1, 30);
185 BOOL onoff = pref->auto_boot ? TRUE : FALSE;
186 EnableWindow(_spin_edit, onoff);
187 EnableWindow(_spin, onoff);
188
189 SET_CHECK(AUTO, pref->auto_boot);
190 if (pref->auto_boot)
191 {
192 TCHAR tmp[32];
193 wsprintf(tmp, TEXT("%d"), pref->auto_boot);
194 Edit_SetText(_spin_edit, tmp);
195 }
196 SET_CHECK(VIDEO, pref->reverse_video);
197 SET_CHECK(PAUSE, pref->pause_before_boot);
198 SET_CHECK(DEBUG, pref->load_debug_info);
199 SET_CHECK(SAFETY, pref->safety_message);
200 }
201
202 virtual void command(int id, int msg) {
203 switch(id) {
204 case IDC_OPT_AUTO:
205 if (IS_CHECKED(AUTO)) {
206 EnableWindow(_spin_edit, TRUE);
207 EnableWindow(_spin, TRUE);
208 } else {
209 EnableWindow(_spin_edit, FALSE);
210 EnableWindow(_spin, FALSE);
211 }
212 break;
213 }
214 }
215
216 void get(void) {
217 HpcMenuInterface &menu = HpcMenuInterface::Instance();
218 struct HpcMenuInterface::HpcMenuPreferences
219 *pref = &menu._pref;
220 if (IS_CHECKED(AUTO)) {
221 TCHAR tmp[32];
222 Edit_GetText(_spin_edit, tmp, 32);
223 pref->auto_boot = _wtoi(tmp);
224 } else
225 pref->auto_boot = 0;
226 pref->reverse_video = IS_CHECKED(VIDEO);
227 pref->pause_before_boot = IS_CHECKED(PAUSE);
228 pref->load_debug_info = IS_CHECKED(DEBUG);
229 pref->safety_message = IS_CHECKED(SAFETY);
230 }
231 };
232
233 //
234 // Console window
235 //
236 class ConsoleTabWindow : public TabWindow
237 {
238 private:
239 HWND _filename_edit;
240 BOOL _filesave;
241 HANDLE _logfile;
242 BOOL _open_log_file(void);
243 public:
244 HWND _edit;
245
246 public:
247 explicit ConsoleTabWindow(TabWindowBase &base, int id)
248 : TabWindow(base, id, TEXT("WConsole")) {
249 _edit = NULL;
250 _logfile = INVALID_HANDLE_VALUE;
251 }
252 virtual ~ConsoleTabWindow(void) {
253 if (_logfile != INVALID_HANDLE_VALUE)
254 CloseHandle(_logfile);
255 }
256 virtual void init(HWND);
257 virtual void command(int, int);
258
259 void print(TCHAR *buf, BOOL = FALSE);
260 };
261
262 void
263 ConsoleTabWindow::print(TCHAR *buf, BOOL force_display)
264 {
265 int cr;
266 TCHAR *p;
267
268 if (force_display)
269 goto display;
270
271 if (_filesave) {
272 if (_logfile == INVALID_HANDLE_VALUE && !_open_log_file()) {
273 _filesave = FALSE;
274 _set_check(IDC_CONS_FILESAVE, _filesave);
275 EnableWindow(_filename_edit, _filesave);
276 goto display;
277 }
278 DWORD cnt;
279 char c;
280 for (int i = 0; *buf != TEXT('\0'); buf++) {
281 c = *buf & 0x7f;
282 WriteFile(_logfile, &c, 1, &cnt, 0);
283 }
284
285 FlushFileBuffers(_logfile);
286 return;
287 }
288
289 display:
290 // count # of '\n'
291 for (cr = 0, p = buf; p = wcschr(p, TEXT('\n')); cr++, p++)
292 ;
293 // total length of new buffer('\n' -> "\r\n" + '\0')
294 int ln = wcslen(buf) + cr + 1;
295
296 // get old buffer.
297 int lo = Edit_GetTextLength(_edit);
298 size_t sz =(lo + ln) * sizeof(TCHAR);
299
300 p = reinterpret_cast <TCHAR *>(malloc(sz));
301 if (p == NULL)
302 return;
303
304 memset(p, 0, sz);
305 Edit_GetText(_edit, p, lo + 1);
306
307 // put new buffer to end of old buffer.
308 TCHAR *d = p + lo;
309 while (*buf != TEXT('\0')) {
310 TCHAR c = *buf++;
311 if (c == TEXT('\n'))
312 *d++ = TEXT('\r');
313 *d++ = c;
314 }
315 *d = TEXT('\0');
316
317 // display total buffer.
318 Edit_SetText(_edit, p);
319 // Edit_Scroll(_edit, Edit_GetLineCount(_edit), 0);
320 UpdateWindow(_edit);
321
322 free(p);
323 }
324
325 void
326 ConsoleTabWindow::init(HWND w)
327 {
328 // at this time _window is NULL.
329 // use argument of window procedure.
330 TabWindow::init(w);
331 _edit = GetDlgItem(w, IDC_CONS_EDIT);
332 MoveWindow(_edit, 5, 60, _rect.right - _rect.left - 10,
333 _rect.bottom - _rect.top - 60, TRUE);
334 Edit_FmtLines(_edit, TRUE);
335
336 // log file.
337 _filename_edit = GetDlgItem(w, IDC_CONS_FILENAME);
338 _filesave = FALSE;
339 Edit_SetText(_filename_edit, L"bootlog.txt");
340 EnableWindow(_filename_edit, _filesave);
341 }
342
343 void
344 ConsoleTabWindow::command(int id, int msg)
345 {
346 HpcMenuInterface &menu = HpcMenuInterface::Instance();
347 struct HpcMenuInterface::cons_hook_args *hook = 0;
348 int bit;
349
350 switch(id) {
351 case IDC_CONS_FILESAVE:
352 _filesave = _is_checked(IDC_CONS_FILESAVE);
353 EnableWindow(_filename_edit, _filesave);
354 break;
355 case IDC_CONS_CHK0:
356 /* FALLTHROUGH */
357 case IDC_CONS_CHK1:
358 /* FALLTHROUGH */
359 case IDC_CONS_CHK2:
360 /* FALLTHROUGH */
361 case IDC_CONS_CHK3:
362 /* FALLTHROUGH */
363 case IDC_CONS_CHK4:
364 /* FALLTHROUGH */
365 case IDC_CONS_CHK5:
366 /* FALLTHROUGH */
367 case IDC_CONS_CHK6:
368 /* FALLTHROUGH */
369 case IDC_CONS_CHK7:
370 bit = 1 << (id - IDC_CONS_CHK_);
371 if (SendDlgItemMessage(_window, id, BM_GETCHECK, 0, 0))
372 menu._cons_parameter |= bit;
373 else
374 menu._cons_parameter &= ~bit;
375 break;
376 case IDC_CONS_BTN0:
377 /* FALLTHROUGH */
378 case IDC_CONS_BTN1:
379 /* FALLTHROUGH */
380 case IDC_CONS_BTN2:
381 /* FALLTHROUGH */
382 case IDC_CONS_BTN3:
383 hook = &menu._cons_hook[id - IDC_CONS_BTN_];
384 if (hook->func)
385 hook->func(hook->arg, menu._cons_parameter);
386
387 break;
388 }
389 }
390
391 BOOL
392 ConsoleTabWindow::_open_log_file()
393 {
394 TCHAR path[MAX_PATH];
395 TCHAR filename[MAX_PATH];
396 TCHAR filepath[MAX_PATH];
397
398 if (!_find_pref_dir(path))
399 print(L"couldn't find temporary directory.\n", TRUE);
400 Edit_GetText(_filename_edit, filename, MAX_PATH);
401 wsprintf(filepath, TEXT("\\%s\\%s"), path, filename);
402 _logfile = CreateFile(filepath, GENERIC_WRITE, 0, 0,
403 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
404 0);
405 if (_logfile == INVALID_HANDLE_VALUE)
406 return FALSE;
407 wsprintf(path, TEXT("log file is %s\n"), filepath);
408 print(path, TRUE);
409
410 return TRUE;
411 }
412
413 TabWindow *
414 TabWindowBase::boot(int id)
415 {
416 TabWindow *w = NULL;
417 HpcMenuInterface &menu = HpcMenuInterface::Instance();
418
419 switch(id) {
420 default:
421 break;
422 case IDC_BASE_MAIN:
423 menu._main = new MainTabWindow(*this, IDC_BASE_MAIN);
424 w = menu._main;
425 break;
426 case IDC_BASE_OPTION:
427 menu._option = new OptionTabWindow(*this, IDC_BASE_OPTION);
428 w = menu._option;
429 break;
430 case IDC_BASE_CONSOLE:
431 menu._console = new ConsoleTabWindow(*this, IDC_BASE_CONSOLE);
432 w = menu._console;
433 break;
434 }
435
436 if (w)
437 w->create(0);
438
439 return w;
440 }
441
442 //
443 // External Interface
444 //
445 HpcMenuInterface *HpcMenuInterface::_instance = 0;
446
447 HpcMenuInterface &
448 HpcMenuInterface::Instance()
449 {
450 if (!_instance)
451 _instance = new HpcMenuInterface();
452 return *_instance;
453 }
454
455 void
456 HpcMenuInterface::Destroy()
457 {
458 if (_instance)
459 delete _instance;
460 }
461
462 void
463 HpcMenuInterface::print(TCHAR *buf)
464 {
465 if (_console)
466 _console->print(buf);
467 }
468
469 void
470 HpcMenuInterface::get_options()
471 {
472 _main->get();
473 _option->get();
474 }
475
476 TCHAR *
477 HpcMenuInterface::dir(int i)
478 {
479 int res = IDS_DIR_RES(i);
480 if (!IDS_DIR_RES_VALID(res))
481 return 0;
482
483 if (_pref.dir_user && res == IDS_DIR_USER_DEFINED) {
484 return _pref.dir_user_path;
485 }
486
487 TCHAR *s = reinterpret_cast <TCHAR *>
488 (LoadString(_root->_app._instance, res, 0, 0));
489
490 return s;
491 }
492
493 int
494 HpcMenuInterface::dir_default()
495 {
496 return _pref.dir_user ? IDS_DIR_SEQ(IDS_DIR_USER_DEFINED) : 0;
497 }
498
499 BOOL
500 HpcMenuInterface::load()
501 {
502 TCHAR path[MAX_PATH];
503
504 if (!_find_pref_dir(path))
505 return FALSE;
506
507 TCHAR filename[MAX_PATH];
508 wsprintf(filename, TEXT("\\%s\\hpcboot.cnf"), path);
509 HANDLE file = CreateFile(filename, GENERIC_READ, 0, 0, OPEN_EXISTING,
510 FILE_ATTRIBUTE_NORMAL, 0);
511 if (file == INVALID_HANDLE_VALUE)
512 return FALSE;
513
514 DWORD cnt;
515 // read header.
516 if (!ReadFile(file, &_pref, 12, &cnt, 0))
517 goto bad;
518 if (_pref._magic != HPCBOOT_MAGIC)
519 goto bad;
520 // read all.
521 SetFilePointer(file, 0, 0, FILE_BEGIN);
522 if (!ReadFile(file, &_pref, _pref._size, &cnt, 0))
523 goto bad;
524 CloseHandle(file);
525
526 return TRUE;
527 bad:
528 CloseHandle(file);
529 return FALSE;
530 }
531
532 BOOL
533 HpcMenuInterface::save()
534 {
535 TCHAR path[MAX_PATH];
536
537 if (_find_pref_dir(path)) {
538 TCHAR filename[MAX_PATH];
539 wsprintf(filename, TEXT("\\%s\\hpcboot.cnf"), path);
540 HANDLE file = CreateFile(filename, GENERIC_WRITE, 0, 0,
541 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
542 0);
543 DWORD cnt;
544 WriteFile(file, &_pref, _pref._size, &cnt, 0);
545 CloseHandle(file);
546 return cnt == _pref._size;
547 }
548
549 return FALSE;
550 }
551
552 // arguments for kernel.
553 int
554 HpcMenuInterface::setup_kernel_args(vaddr_t v, paddr_t p)
555 {
556 int argc = 0;
557 kaddr_t *argv = reinterpret_cast <kaddr_t *>(v);
558 char *loc = reinterpret_cast <char *>
559 (v + sizeof(char **) * MAX_KERNEL_ARGS);
560 paddr_t locp = p + sizeof(char **) * MAX_KERNEL_ARGS;
561 size_t len;
562 TCHAR *w;
563
564 #define SETOPT(c) \
565 __BEGIN_MACRO \
566 argv[argc++] = ptokv(locp); \
567 *loc++ =(c); \
568 *loc++ = '\0'; \
569 locp += 2; \
570 __END_MACRO
571 // 1st arg is kernel name.
572 // DPRINTF_SETUP(); if you want to use debug print, enable this line.
573
574 w = _pref.kernel_user_file;
575 argv[argc++] = ptokv(locp);
576 len = WideCharToMultiByte(CP_ACP, 0, w, wcslen(w), 0, 0, 0, 0);
577 WideCharToMultiByte(CP_ACP, 0, w, len, loc, len, 0, 0);
578 loc[len] = '\0';
579 loc += len + 1;
580 locp += len + 1;
581
582 if (_pref.boot_serial) // serial console
583 SETOPT('h');
584 if (_pref.boot_verbose) // boot verbosely
585 SETOPT('v');
586 if (_pref.boot_single_user) // boot to single user
587 SETOPT('s');
588 if (_pref.boot_ask_for_name) // ask for file name to boot from
589 SETOPT('a');
590
591 // boot from
592 switch(_pref.rootfs) {
593 case 0: // wd0
594 break;
595 case 1: // sd0
596 argv[argc++] = ptokv(locp);
597 strncpy(loc, "b=sd0", 6);
598 loc += 6;
599 locp += 6;
600 break;
601 case 2: // memory disk
602 w = _pref.rootfs_file;
603 argv[argc++] = ptokv(locp);
604 strncpy(loc, "m=", 2);
605 loc += 2;
606 len = WideCharToMultiByte(CP_ACP, 0, w, wcslen(w), 0, 0, 0, 0);
607 WideCharToMultiByte(CP_ACP, 0, w, len, loc, len, 0, 0);
608 loc[len] = '\0';
609 loc += len + 1;
610 locp += 2 + len + 1;
611 break;
612 case 3: // nfs
613 argv[argc++] = ptokv(locp);
614 strncpy(loc, "b=nfs", 6);
615 loc += 6;
616 locp += 6;
617 break;
618 }
619
620 return argc;
621 }
622
623 // kernel bootinfo.
624 void
625 HpcMenuInterface::setup_bootinfo(struct bootinfo &bi)
626 {
627 FrameBufferInfo fb(_pref.platid_hi, _pref.platid_lo);
628 TIME_ZONE_INFORMATION tz;
629 GetTimeZoneInformation(&tz);
630
631 memset(&bi, 0, sizeof(struct bootinfo));
632 bi.length = sizeof(struct bootinfo);
633 bi.reserved = 0;
634 bi.magic = BOOTINFO_MAGIC;
635 bi.fb_addr = fb.addr();
636 bi.fb_type = fb.type();
637 bi.fb_line_bytes = fb.linebytes();
638 bi.fb_width = fb.width();
639 bi.fb_height = fb.height();
640 bi.platid_cpu = _pref.platid_hi;
641 bi.platid_machine = _pref.platid_lo;
642 bi.timezone = tz.Bias;
643 }
644
645 // Progress bar
646 void
647 HpcMenuInterface::progress()
648 {
649 SendMessage(_root->_progress_bar->_window, PBM_STEPIT, 0, 0);
650 }
651
652 void
653 HpcMenuInterface::boot()
654 {
655 struct support_status *tab = _unsupported;
656 u_int32_t cpu = _pref.platid_hi;
657 u_int32_t machine = _pref.platid_lo;
658
659 if (_pref.safety_message)
660 for (; tab->cpu; tab++) {
661 if (tab->cpu == cpu && tab->machine == machine) {
662 MessageBox(_root->_window,
663 tab->cause ? tab->cause :
664 L"not supported yet.",
665 TEXT("BOOT FAILED"), 0);
666 return;
667 }
668 }
669
670 if (_boot_hook.func)
671 _boot_hook.func(_boot_hook.arg, _pref);
672 }
673
674 //
675 // Common utility
676 //
677 static BOOL
678 _find_pref_dir(TCHAR *path)
679 {
680 WIN32_FIND_DATA fd;
681 HANDLE find;
682
683 lstrcpy(path, TEXT("\\*.*"));
684 find = FindFirstFile(path, &fd);
685
686 if (find != INVALID_HANDLE_VALUE) {
687 do {
688 int attr = fd.dwFileAttributes;
689 if ((attr & FILE_ATTRIBUTE_DIRECTORY) &&
690 (attr & FILE_ATTRIBUTE_TEMPORARY)) {
691 wcscpy(path, fd.cFileName);
692 FindClose(find);
693 return TRUE;
694 }
695 } while (FindNextFile(find, &fd));
696 }
697 FindClose(find);
698
699 return FALSE;
700 }
701