OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / win / Qt4 / qt4bind.cpp
1 // Copyright (c) Warwick Allison, 1999.
2 // Qt4 conversion copyright (c) Ray Chason, 2012-2014.
3 // NetHack may be freely redistributed.  See license for details.
4
5 // qt4bind.cpp -- bindings between the Qt 4 interface and the main code
6
7 extern "C" {
8 #include "hack.h"
9 }
10 #undef Invisible
11 #undef Warning
12 #undef index
13 #undef msleep
14 #undef rindex
15 #undef wizard
16 #undef yn
17 #undef min
18 #undef max
19
20 #include <QtGui/QtGui>
21 #include <QStringList>
22 #if QT_VERSION >= 0x050000
23 #include <QtWidgets/QtWidgets>
24 #include <QtMultimedia/QSound>
25 #else
26 #include <QtGui/QSound>
27 #endif
28 #include "qt4bind.h"
29 #include "qt4click.h"
30 #ifdef TIMED_DELAY
31 #include "qt4delay.h"
32 #endif
33 #include "qt4xcmd.h"
34 #include "qt4key.h"
35 #include "qt4map.h"
36 #include "qt4menu.h"
37 #include "qt4msg.h"
38 #include "qt4plsel.h"
39 #include "qt4svsel.h"
40 #include "qt4set.h"
41 #include "qt4stat.h"
42 #include "qt4streq.h"
43 #include "qt4yndlg.h"
44 #include "qt4str.h"
45
46 extern "C" {
47 #include "dlb.h"
48 }
49
50 // temporary
51 extern int qt_compact_mode;
52 // end temporary
53
54 namespace nethack_qt4 {
55
56 // XXX Should be from Options
57 //
58 // XXX Hmm.  Tricky part is that perhaps some macros should only be active
59 // XXX       when a key is about to be gotten.  For example, the user could
60 // XXX       define "-" to do "E-yyyyyyyy\r", but would still need "-" for
61 // XXX       other purposes.  Maybe just too bad.
62 //
63 static struct key_macro_rec {
64     int key;
65     int state;
66     const char* macro;
67 } key_macro[]={
68     { Qt::Key_F1, 0, "n100." }, // Rest (x100)
69     { Qt::Key_F2, 0, "n20s" },  // Search (x20)
70     { Qt::Key_Tab, 0, "\001" },
71     { 0, 0, 0 }
72 };
73
74 NetHackQtBind::NetHackQtBind(int& argc, char** argv) :
75 #ifdef KDE
76     KApplication(argc,argv)
77 #elif defined(QWS) // not quite the right condition
78     QPEApplication(argc,argv)
79 #else
80     QApplication(argc,argv)
81 #endif
82 {
83     QPixmap pm("nhsplash.xpm");
84     if ( iflags.wc_splash_screen && !pm.isNull() ) {
85         splash = new QFrame(NULL,
86             Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint );
87         QVBoxLayout *vb = new QVBoxLayout(splash);
88         QLabel *lsplash = new QLabel(splash);
89         vb->addWidget(lsplash);
90         lsplash->setAlignment(Qt::AlignCenter);
91         lsplash->setPixmap(pm);
92         QLabel* capt = new QLabel("Loading...",splash);
93         vb->addWidget(capt);
94         capt->setAlignment(Qt::AlignCenter);
95         if ( !pm.isNull() ) {
96             lsplash->setFixedSize(pm.size());
97             lsplash->setMask(pm);
98         }
99         splash->move((QApplication::desktop()->width()-pm.width())/2,
100                       (QApplication::desktop()->height()-pm.height())/2);
101         //splash->setGeometry(0,0,100,100);
102         if ( qt_compact_mode ) {
103             splash->showMaximized();
104         } else {
105             splash->setFrameStyle(QFrame::WinPanel|QFrame::Raised);
106             splash->setLineWidth(10);
107             splash->adjustSize();
108             splash->show();
109         }
110
111         // force content refresh outside event loop
112         splash->repaint();
113         lsplash->repaint();
114         capt->repaint();
115         qApp->flush();
116
117     } else {
118         splash = 0;
119     }
120     main = new NetHackQtMainWindow(keybuffer);
121     connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()));
122     qt_settings=new NetHackQtSettings(main->width(),main->height());
123     msgs_strings = new QStringList();
124     msgs_initd = false;
125     msgs_saved = false;
126 }
127
128 void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv)
129 {
130     iflags.menu_tab_sep = true;
131
132 #ifdef UNIX
133 // Userid control
134 //
135 // Michael Hohmuth <hohmuth@inf.tu-dresden.de>...
136 //
137 // As the game runs setuid games, it must seteuid(getuid()) before
138 // calling XOpenDisplay(), and reset the euid afterwards.
139 // Otherwise, it can't read the $HOME/.Xauthority file and whines about
140 // not being able to open the X display (if a magic-cookie
141 // authorization mechanism is being used). 
142
143     uid_t gamesuid=geteuid();
144     seteuid(getuid());
145 #endif
146
147     QApplication::setColorSpec(ManyColor);
148     instance=new NetHackQtBind(*argc,argv);
149
150 #ifdef UNIX
151     seteuid(gamesuid);
152 #endif
153
154 #ifdef _WS_WIN_
155     // This nethack engine feature should be moved into windowport API
156     nt_kbhit = NetHackQtBind::qt_kbhit;
157 #endif
158 }
159
160 int NetHackQtBind::qt_kbhit()
161 {
162     return !keybuffer.Empty();
163 }
164
165
166 static bool have_asked = false;
167
168 void NetHackQtBind::qt_player_selection()
169 {
170     if ( !have_asked )
171         qt_askname();
172 }
173
174 void NetHackQtBind::qt_askname()
175 {
176     have_asked = true;
177
178     // We do it all here, and nothing in askname
179
180     char** saved = get_saved_games();
181     int ch = -1;
182     if ( saved && *saved ) {
183         if ( splash ) splash->hide();
184         NetHackQtSavedGameSelector sgsel((const char**)saved);
185         ch = sgsel.choose();
186         if ( ch >= 0 )
187             str_copy(plname, saved[ch], SIZE(plname));
188     }
189     free_saved_games(saved);
190
191     switch (ch) {
192       case -1:
193         if ( splash ) splash->hide();
194         if (NetHackQtPlayerSelector(keybuffer).Choose())
195             return;
196       case -2:
197         break;
198       default:
199         return;
200     }
201
202     // Quit
203     clearlocks();
204     qt_exit_nhwindows(0);
205     nh_terminate(0);
206 }
207
208 void NetHackQtBind::qt_get_nh_event()
209 {
210 }
211
212 #if defined(QWS)
213 // Kludge to access lastWindowClosed() signal.
214 class TApp : public QApplication {
215 public:
216     TApp(int& c, char**v) : QApplication(c,v) {}
217     void lwc() { emit lastWindowClosed(); }
218 };
219 #endif
220  
221 void NetHackQtBind::qt_exit_nhwindows(const char *)
222 {
223 #if defined(QWS)
224     // Avoids bug in SHARP SL5500
225     ((TApp*)qApp)->lwc();
226     qApp->quit();
227 #endif
228  
229     delete instance; // ie. qApp
230 }
231
232 void NetHackQtBind::qt_suspend_nhwindows(const char *)
233 {
234 }
235
236 void NetHackQtBind::qt_resume_nhwindows()
237 {
238 }
239
240 static QVector<NetHackQtWindow*> id_to_window;
241
242 winid NetHackQtBind::qt_create_nhwindow(int type)
243 {
244     winid id;
245     for (id = 0; id < (winid) id_to_window.size(); id++) {
246         if ( !id_to_window[(int)id] )
247             break;
248     }
249     if ( id == (winid) id_to_window.size() )
250         id_to_window.resize(id+1);
251
252     NetHackQtWindow* window=0;
253
254     switch (type) {
255      case NHW_MAP: {
256         NetHackQtMapWindow2* w=new NetHackQtMapWindow2(clickbuffer);
257         main->AddMapWindow(w);
258         window=w;
259     } break; case NHW_MESSAGE: {
260         NetHackQtMessageWindow* w=new NetHackQtMessageWindow;
261         main->AddMessageWindow(w);
262         window=w;
263     } break; case NHW_STATUS: {
264         NetHackQtStatusWindow* w=new NetHackQtStatusWindow;
265         main->AddStatusWindow(w);
266         window=w;
267     } break; case NHW_MENU:
268         window=new NetHackQtMenuOrTextWindow(mainWidget());
269     break; case NHW_TEXT:
270         window=new NetHackQtTextWindow(mainWidget());
271     }
272
273     window->nhid = id;
274
275     // Note: use of isHidden does not work with Qt 2.1
276     if ( splash 
277 #if QT_VERSION >= 300
278         && !main->isHidden()
279 #else
280         && main->isVisible()
281 #endif
282         )
283     {
284         delete splash;
285         splash = 0;
286     }
287
288     id_to_window[(int)id] = window;
289     return id;
290 }
291
292 void NetHackQtBind::qt_clear_nhwindow(winid wid)
293 {
294     NetHackQtWindow* window=id_to_window[(int)wid];
295     window->Clear();
296 }
297
298 void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block)
299 {
300     NetHackQtWindow* window=id_to_window[(int)wid];
301     window->Display(block);
302 }
303
304 void NetHackQtBind::qt_destroy_nhwindow(winid wid)
305 {
306     NetHackQtWindow* window=id_to_window[(int)wid];
307     main->RemoveWindow(window);
308     if (window->Destroy())
309         delete window;
310     id_to_window[(int)wid] = 0;
311 }
312
313 void NetHackQtBind::qt_curs(winid wid, int x, int y)
314 {
315     NetHackQtWindow* window=id_to_window[(int)wid];
316     window->CursorTo(x,y);
317 }
318
319 void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text)
320 {
321     NetHackQtWindow* window=id_to_window[(int)wid];
322     window->PutStr(attr,QString::fromLatin1(text));
323 }
324
325 void NetHackQtBind::qt_putstr(winid wid, int attr, const std::string& text)
326 {
327     NetHackQtWindow* window=id_to_window[(int)wid];
328     window->PutStr(attr,QString::fromLatin1(text.c_str(), text.size()));
329 }
330
331 void NetHackQtBind::qt_putstr(winid wid, int attr, const QString& text)
332 {
333     NetHackQtWindow* window=id_to_window[(int)wid];
334     window->PutStr(attr,text);
335 }
336
337 void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist)
338 {
339     NetHackQtTextWindow* window=new NetHackQtTextWindow(mainWidget());
340     bool complain = false;
341
342     {
343         dlb *f;
344         char buf[BUFSZ];
345         char *cr;
346
347         window->Clear();
348         f = dlb_fopen(filename, "r");
349         if (!f) {
350             complain = must_exist;
351         } else {
352             while (dlb_fgets(buf, BUFSZ, f)) {
353                 if ((cr = index(buf, '\n')) != 0) *cr = 0;
354 #ifdef MSDOS
355                 if ((cr = index(buf, '\r')) != 0) *cr = 0;
356 #endif
357                 window->PutStr(ATR_NONE, tabexpand(buf));
358             }
359             window->Display(false);
360             (void) dlb_fclose(f);
361         }
362     }
363
364     if (complain) {
365         QString message;
366         message.sprintf("File not found: %s\n",filename);
367         QMessageBox::warning(NULL, "File Error", message, QMessageBox::Ignore);
368     }
369 }
370
371 void NetHackQtBind::qt_start_menu(winid wid)
372 {
373     NetHackQtWindow* window=id_to_window[(int)wid];
374     window->StartMenu();
375 }
376
377 void NetHackQtBind::qt_add_menu(winid wid, int glyph,
378     const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr,
379     const char *str, BOOLEAN_P presel)
380 {
381     NetHackQtWindow* window=id_to_window[(int)wid];
382     window->AddMenu(glyph, identifier, ch, gch, attr,
383             QString::fromLatin1(str),
384             presel);
385 }
386
387 void NetHackQtBind::qt_end_menu(winid wid, const char *prompt)
388 {
389     NetHackQtWindow* window=id_to_window[(int)wid];
390     window->EndMenu(prompt);
391 }
392
393 int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list)
394 {
395     NetHackQtWindow* window=id_to_window[(int)wid];
396     return window->SelectMenu(how,menu_list);
397 }
398
399 void NetHackQtBind::qt_update_inventory()
400 {
401     if (main)
402         main->updateInventory();
403     /* doesn't work yet
404     if (program_state.something_worth_saving && flags.perm_invent)
405         display_inventory(NULL, false);
406     */
407 }
408
409 void NetHackQtBind::qt_mark_synch()
410 {
411 }
412
413 void NetHackQtBind::qt_wait_synch()
414 {
415 }
416
417 void NetHackQtBind::qt_cliparound(int x, int y)
418 {
419     // XXXNH - winid should be a parameter!
420     qt_cliparound_window(WIN_MAP,x,y);
421 }
422
423 void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y)
424 {
425     NetHackQtWindow* window=id_to_window[(int)wid];
426     window->ClipAround(x,y);
427 }
428 void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph)
429 {
430     /* TODO: bkglyph */
431     NetHackQtWindow* window=id_to_window[(int)wid];
432     window->PrintGlyph(x,y,glyph);
433 }
434 //void NetHackQtBind::qt_print_glyph_compose(winid wid,xchar x,xchar y,int glyph1, int glyph2)
435 //{
436     //NetHackQtWindow* window=id_to_window[(int)wid];
437     //window->PrintGlyphCompose(x,y,glyph1,glyph2);
438 //}
439
440 void NetHackQtBind::qt_raw_print(const char *str)
441 {
442     puts(str);
443 }
444
445 void NetHackQtBind::qt_raw_print_bold(const char *str)
446 {
447     puts(str);
448 }
449
450 int NetHackQtBind::qt_nhgetch()
451 {
452     if (main)
453         main->fadeHighlighting();
454
455     // Process events until a key arrives.
456     //
457     while (keybuffer.Empty()) {
458         qApp->exec();
459     }
460
461     return keybuffer.GetAscii();
462 }
463
464 int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod)
465 {
466     if (main)
467         main->fadeHighlighting();
468
469     // Process events until a key or map-click arrives.
470     //
471     while (keybuffer.Empty() && clickbuffer.Empty()) {
472         qApp->exec();
473     }
474     if (!keybuffer.Empty()) {
475         return keybuffer.GetAscii();
476     } else {
477         *x=clickbuffer.NextX();
478         *y=clickbuffer.NextY();
479         *mod=clickbuffer.NextMod();
480         clickbuffer.Get();
481         return 0;
482     }
483 }
484
485 void NetHackQtBind::qt_nhbell()
486 {
487     QApplication::beep();
488 }
489
490 int NetHackQtBind::qt_doprev_message()
491 {
492     // Don't need it - uses scrollbar
493     // XXX but could make this a shortcut
494     return 0;
495 }
496
497 char NetHackQtBind::qt_yn_function(const char *question_, const char *choices, CHAR_P def)
498 {
499     QString question(QString::fromLatin1(question_));
500     QString message;
501     char yn_esc_map='\033';
502
503     if (choices) {
504         // anything beyond <esc> is hidden>
505         QString choicebuf = choices;
506         size_t cb = choicebuf.indexOf('\033');
507         choicebuf = choicebuf.mid(0U, cb);
508         message = QString("%1 [%2] ").arg(question, choicebuf);
509         if (def) message += QString("(%1) ").arg(QChar(def));
510         // escape maps to 'q' or 'n' or default, in that order
511         yn_esc_map = (index(choices, 'q') ? 'q' :
512                       (index(choices, 'n') ? 'n' : def));
513     } else {
514         message = question;
515     }
516
517     if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) {
518         // Similar to X11 windowport `slow' feature.
519
520         int result = -1;
521
522 #ifdef USE_POPUPS
523         if (choices) {
524             if (!strcmp(choices,"ynq"))
525                 result = QMessageBox::information (NetHackQtBind::mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2);
526             else if (!strcmp(choices,"yn"))
527                 result = QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Yes", "&No",0,1);
528             else if (!strcmp(choices, "rl"))
529                 result = QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Right", "&Left",0,1);
530
531             if (result >= 0 && result < strlen(choices)) {
532                 char yn_resp = choices[result];
533                 message += QString(" %1").arg(yn_resp);
534                 result = yn_resp;
535             }
536         }
537 #endif
538
539         NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
540
541         while (result < 0) {
542             char ch=NetHackQtBind::qt_nhgetch();
543             if (ch=='\033') {
544                 result=yn_esc_map;
545             } else if (choices && !index(choices,ch)) {
546                 if (def && (ch==' ' || ch=='\r' || ch=='\n')) {
547                     result=def;
548                 } else {
549                     NetHackQtBind::qt_nhbell();
550                     // and try again...
551                 }
552             } else {
553                 result=ch;
554             }
555         }
556
557         NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE);
558
559         return result;
560     } else {
561         NetHackQtYnDialog dialog(mainWidget(),question,choices,def);
562         char ret = dialog.Exec();
563         if (!(ret == '\0' || ret == '\033') && choices)
564             message += QString(" %1").arg(ret);
565         else if (def)
566             message += QString(" %1").arg(def);
567         NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message);
568     }
569 }
570
571 void NetHackQtBind::qt_getlin(const char *prompt, char *line)
572 {
573     NetHackQtStringRequestor requestor(mainWidget(),prompt);
574     if (!requestor.Get(line)) {
575         line[0]=0;
576     }
577 }
578
579 int NetHackQtBind::qt_get_ext_cmd()
580 {
581     NetHackQtExtCmdRequestor requestor(mainWidget());
582     return requestor.get();
583 }
584
585 void NetHackQtBind::qt_number_pad(int)
586 {
587     // Ignore.
588 }
589
590 void NetHackQtBind::qt_delay_output()
591 {
592 #ifdef TIMED_DELAY
593     NetHackQtDelay delay(50);
594     delay.wait();
595 #endif
596 }
597
598 void NetHackQtBind::qt_start_screen()
599 {
600     // Ignore.
601 }
602
603 void NetHackQtBind::qt_end_screen()
604 {
605     // Ignore.
606 }
607
608 void NetHackQtBind::qt_outrip(winid wid, int how, time_t when)
609 {
610     NetHackQtWindow* window=id_to_window[(int)wid];
611     window->UseRIP(how, when);
612 }
613
614 char * NetHackQtBind::qt_getmsghistory(BOOLEAN_P init)
615 {
616     NetHackQtMessageWindow* window = main->GetMessageWindow();
617     if (window)
618         return (char *)window->GetStr(init);
619     return NULL;
620 }
621
622 void NetHackQtBind::qt_putmsghistory(const char *msg, BOOLEAN_P is_restoring)
623 {
624     NetHackQtMessageWindow* window = main->GetMessageWindow();
625     if (!window)
626         return;
627
628     if (is_restoring && !msgs_initd) {
629         /* we're restoring history from the previous session, but new
630            messages have already been issued this session */
631         int i = 0;
632         const char *str;
633
634         while ((str = window->GetStr((i == 0)))) {
635             msgs_strings->append(str);
636             i++;
637         }
638         msgs_initd = true;
639         msgs_saved = (i > 0);
640         window->ClearMessages();
641     }
642
643     if (msg) {
644         //raw_printf("msg='%s'", msg);
645         window->PutStr(ATR_NONE, QString::fromLatin1(msg));
646 #ifdef DUMPLOG
647         dumplogmsg(msg);
648 #endif
649     } else if (msgs_saved) {
650         /* restore strings */
651         int i;
652         for (i = 0; i < msgs_strings->size(); i++) {
653             window->PutStr(ATR_NONE, msgs_strings->at((i)));
654 #ifdef DUMPLOG
655             dumplogmsg(msgs_strings->at(i).toLatin1().constData());
656 #endif
657         }
658         delete msgs_strings;
659         msgs_initd = false;
660     }
661 }
662
663 bool NetHackQtBind::notify(QObject *receiver, QEvent *event)
664 {
665     // Ignore Alt-key navigation to menubar, it's annoying when you
666     // use Alt-Direction to move around.
667     if ( main && event->type()==QEvent::KeyRelease && main==receiver
668             && ((QKeyEvent*)event)->key() == Qt::Key_Alt )
669         return true;
670
671     bool result=QApplication::notify(receiver,event);
672     if (event->type()==QEvent::KeyPress) {
673         QKeyEvent* key_event=(QKeyEvent*)event;
674
675         if (!key_event->isAccepted()) {
676             const int k=key_event->key();
677             bool macro=false;
678             for (int i=0; !macro && key_macro[i].key; i++) {
679                 if (key_macro[i].key==k
680                  && ((key_macro[i].state&key_event->modifiers())==key_macro[i].state))
681                 {
682                     keybuffer.Put(key_macro[i].macro);
683                     macro=true;
684                 }
685             }
686             QString key=key_event->text();
687             QChar ch = !key.isEmpty() ? key.at(0) : 0;
688             if (ch > 128) ch = 0;
689             if ( ch == 0 && (key_event->modifiers() & Qt::ControlModifier) ) {
690                 // On Mac, ascii control codes are not sent, force them.
691                 if ( k>=Qt::Key_A && k<=Qt::Key_Z )
692                     ch = k - Qt::Key_A + 1;
693             }
694             if (!macro && ch != 0) {
695                 bool alt = (key_event->modifiers()&Qt::AltModifier) ||
696                    (k >= Qt::Key_0 && k <= Qt::Key_9 && (key_event->modifiers()&Qt::ControlModifier));
697                 keybuffer.Put(key_event->key(),ch.cell() + (alt ? 128 : 0),
698                     key_event->modifiers());
699                 key_event->accept();
700                 result=true;
701             }
702
703             if (ch != 0 || macro) {
704                 qApp->exit();
705             }
706         }
707     }
708     return result;
709 }
710
711 NetHackQtBind* NetHackQtBind::instance=0;
712 NetHackQtKeyBuffer NetHackQtBind::keybuffer;
713 NetHackQtClickBuffer NetHackQtBind::clickbuffer;
714 NetHackQtMainWindow* NetHackQtBind::main=0;
715 QFrame* NetHackQtBind::splash=0;
716 QStringList *NetHackQtBind::msgs_strings;
717 boolean NetHackQtBind::msgs_saved = false;
718 boolean NetHackQtBind::msgs_initd = false;
719
720 static void Qt_positionbar(char *) {}
721
722 } // namespace nethack_qt4
723
724 struct window_procs Qt_procs = {
725     "Qt",
726     WC_COLOR | WC_HILITE_PET
727     | WC_ASCII_MAP | WC_TILED_MAP
728     | WC_FONT_MAP | WC_TILE_FILE | WC_TILE_WIDTH | WC_TILE_HEIGHT
729     | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN,
730     0L,
731     nethack_qt4::NetHackQtBind::qt_init_nhwindows,
732     nethack_qt4::NetHackQtBind::qt_player_selection,
733     nethack_qt4::NetHackQtBind::qt_askname,
734     nethack_qt4::NetHackQtBind::qt_get_nh_event,
735     nethack_qt4::NetHackQtBind::qt_exit_nhwindows,
736     nethack_qt4::NetHackQtBind::qt_suspend_nhwindows,
737     nethack_qt4::NetHackQtBind::qt_resume_nhwindows,
738     nethack_qt4::NetHackQtBind::qt_create_nhwindow,
739     nethack_qt4::NetHackQtBind::qt_clear_nhwindow,
740     nethack_qt4::NetHackQtBind::qt_display_nhwindow,
741     nethack_qt4::NetHackQtBind::qt_destroy_nhwindow,
742     nethack_qt4::NetHackQtBind::qt_curs,
743     nethack_qt4::NetHackQtBind::qt_putstr,
744     genl_putmixed,
745     nethack_qt4::NetHackQtBind::qt_display_file,
746     nethack_qt4::NetHackQtBind::qt_start_menu,
747     nethack_qt4::NetHackQtBind::qt_add_menu,
748     nethack_qt4::NetHackQtBind::qt_end_menu,
749     nethack_qt4::NetHackQtBind::qt_select_menu,
750     genl_message_menu,      /* no need for X-specific handling */
751     nethack_qt4::NetHackQtBind::qt_update_inventory,
752     nethack_qt4::NetHackQtBind::qt_mark_synch,
753     nethack_qt4::NetHackQtBind::qt_wait_synch,
754 #ifdef CLIPPING
755     nethack_qt4::NetHackQtBind::qt_cliparound,
756 #endif
757 #ifdef POSITIONBAR
758     nethack_qt4::Qt_positionbar,
759 #endif
760     nethack_qt4::NetHackQtBind::qt_print_glyph,
761     //NetHackQtBind::qt_print_glyph_compose,
762     nethack_qt4::NetHackQtBind::qt_raw_print,
763     nethack_qt4::NetHackQtBind::qt_raw_print_bold,
764     nethack_qt4::NetHackQtBind::qt_nhgetch,
765     nethack_qt4::NetHackQtBind::qt_nh_poskey,
766     nethack_qt4::NetHackQtBind::qt_nhbell,
767     nethack_qt4::NetHackQtBind::qt_doprev_message,
768     nethack_qt4::NetHackQtBind::qt_yn_function,
769     nethack_qt4::NetHackQtBind::qt_getlin,
770     nethack_qt4::NetHackQtBind::qt_get_ext_cmd,
771     nethack_qt4::NetHackQtBind::qt_number_pad,
772     nethack_qt4::NetHackQtBind::qt_delay_output,
773 #ifdef CHANGE_COLOR     /* only a Mac option currently */
774     donull,
775     donull,
776     donull,
777     donull,
778 #endif
779     /* other defs that really should go away (they're tty specific) */
780     nethack_qt4::NetHackQtBind::qt_start_screen,
781     nethack_qt4::NetHackQtBind::qt_end_screen,
782 #ifdef GRAPHIC_TOMBSTONE
783     nethack_qt4::NetHackQtBind::qt_outrip,
784 #else
785     genl_outrip,
786 #endif
787     genl_preference_update,
788
789     nethack_qt4::NetHackQtBind::qt_getmsghistory,
790     nethack_qt4::NetHackQtBind::qt_putmsghistory,
791     genl_status_init,
792     genl_status_finish, genl_status_enablefield,
793 #ifdef STATUS_HILITES
794     genl_status_update,
795 #else
796     genl_status_update,
797 #endif
798     genl_can_suspend_yes,
799 };
800
801 extern "C" void play_usersound(const char* filename, int volume)
802 {
803 #ifdef USER_SOUNDS
804 #ifndef QT_NO_SOUND
805     QSound::play(filename);
806 #endif
807 #endif
808 }