1 /* SCCS Id: @(#)amiwind.c 3.2 2000/01/12
2 /* Copyright (c) Olaf Seibert (KosmoSoft), 1989, 1992 */
3 /* Copyright (c) Kenneth Lorber, Bethesda, Maryland 1993,1996 */
4 /* NetHack may be freely redistributed. See license for details. */
6 #include "NH:sys/amiga/windefs.h"
7 #include "NH:sys/amiga/winext.h"
8 #include "NH:sys/amiga/winproto.h"
10 /* Have to undef CLOSE as display.h and intuition.h both use it */
13 #ifdef AMII_GRAPHICS /* too early in the file? too late? */
16 static struct Message *FDECL(GetFMsg,(struct MsgPort *));
19 static int BufferGetchar(void);
20 static void ProcessMessage( register struct IntuiMessage *message );
22 #define BufferQueueChar(ch) (KbdBuffer[KbdBuffered++] = (ch))
24 struct Library *ConsoleDevice;
26 #include "NH:sys/amiga/amimenu.c"
28 /* Now our own variables */
30 struct IntuitionBase *IntuitionBase;
31 struct Screen *HackScreen;
32 struct Window *pr_WindowPtr;
33 struct MsgPort *HackPort;
34 struct IOStdReq ConsoleIO;
35 struct Menu *MenuStrip;
41 struct GfxBase *GfxBase;
42 struct Library *DiskfontBase;
46 static unsigned char KbdBuffer[KBDBUFFER];
47 unsigned char KbdBuffered;
51 struct TextFont *TextsFont = NULL;
52 struct TextFont *HackFont = NULL;
53 struct TextFont *RogueFont = NULL;
55 UBYTE FontName[] = "NetHack:hack.font";
56 /* # chars in "NetHack:": */
57 #define SIZEOF_DISKNAME 8
61 struct TextAttr Hack80 = {
63 &FontName[SIZEOF_DISKNAME],
65 (UBYTE *) "topaz.font",
67 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED
71 struct TextAttr TextsFont13 = {
72 (UBYTE *) "courier.font",
73 13, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED
79 /* Avoid doing a ReplyMsg through a window that no longer exists. */
80 static enum {NoAction, CloseOver} delayed_key_action = NoAction;
83 * Open a window that shares the HackPort IDCMP. Use CloseShWindow()
87 struct Window *OpenShWindow(nw)
90 register struct Window *win;
91 register ULONG idcmpflags;
93 if (!HackPort) /* Sanity check */
94 return (struct Window *) 0;
96 idcmpflags = nw->IDCMPFlags;
98 if (!(win = OpenWindow((void *)nw)))
100 nw->IDCMPFlags = idcmpflags;
101 return (struct Window *) 0;
104 nw->IDCMPFlags = idcmpflags;
105 win->UserPort = HackPort;
106 ModifyIDCMP(win, idcmpflags);
112 * Close a window that shared the HackPort IDCMP port.
115 void FDECL(CloseShWindow, (struct Window *));
116 void CloseShWindow(win)
119 register struct IntuiMessage *msg;
122 panic("HackPort NULL in CloseShWindow" );
127 /* Flush all messages for all windows to avoid typeahead and other
128 * similar problems...
130 while( msg = (struct IntuiMessage *)GetMsg( win->UserPort ) )
131 ReplyMsg( (struct Message *) msg );
133 win->UserPort = (struct MsgPort *) 0;
134 ModifyIDCMP(win, 0L);
139 static int BufferGetchar()
143 if (KbdBuffered > 0) {
146 /* Move the remaining characters */
147 if( KbdBuffered < sizeof( KbdBuffer ) )
148 memcpy( KbdBuffer, KbdBuffer+1, KbdBuffered );
156 * This should remind you remotely of DeadKeyConvert, but we are cheating
157 * a bit. We want complete control over the numeric keypad, and no dead
158 * keys... (they are assumed to be on Alted keys).
160 * Also assumed is that the IntuiMessage is of type RAWKEY. For some
161 * reason, IECODE_UP_PREFIX events seem to be lost when they occur while
162 * our console window is inactive. This is particulary troublesome with
163 * qualifier keys... Is this because I never RawKeyConvert those events???
166 int ConvertKey(message)
167 register struct IntuiMessage *message;
169 static struct InputEvent theEvent;
170 static char numpad[] = "bjnh.lyku";
171 static char ctrl_numpad[] = "\x02\x0A\x0E\x08.\x0C\x19\x0B\x15";
172 static char shift_numpad[] = "BJNH.LYKU";
174 unsigned char buffer[10];
175 struct Window *w = message->IDCMPWindow;
177 register ULONG qualifier;
178 char numeric_pad, shift, control, alt;
180 if( amii_wins[ WIN_MAP ] )
181 w = amii_wins[ WIN_MAP ]->win;
182 qualifier = message->Qualifier;
184 control = (qualifier & IEQUALIFIER_CONTROL) != 0;
185 shift = (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) != 0;
186 alt = (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT )) != 0;
188 /* Allow ALT to function as a META key ... */
189 /* But make it switchable - alt is needed for some non-US keymaps */
191 qualifier &= ~(IEQUALIFIER_LALT | IEQUALIFIER_RALT);
192 numeric_pad = (qualifier & IEQUALIFIER_NUMERICPAD) != 0;
195 * Shortcut for HELP and arrow keys. I suppose this is allowed.
196 * The defines are in intuition/intuition.h, and the keys don't
197 * serve 'text' input, normally. Also, parsing their escape
198 * sequences is such a mess...
201 switch (message->Code) {
209 else if( WINVERS_AMIV && control )
213 CO = ( w->Width - w->BorderLeft - w->BorderRight ) / mxsize;
214 LI = ( w->Height - w->BorderTop - w->BorderBottom ) / mysize;
215 clipxmax = CO + clipx;
216 clipymax = LI + clipy;
217 if( CO < COLNO || LI < ROWNO )
220 amii_cliparound( u.ux, u.uy );
227 BufferQueueChar( 'R'-64 );
231 else if( WINVERS_AMIV && shift )
233 if( WIN_OVER == WIN_ERR )
235 WIN_OVER = amii_create_nhwindow( NHW_OVER );
236 BufferQueueChar( 'R'-64 );
240 delayed_key_action = CloseOver;
264 theEvent.ie_Class = IECLASS_RAWKEY;
265 theEvent.ie_Code = message->Code;
266 theEvent.ie_Qualifier = numeric_pad ? IEQUALIFIER_NUMERICPAD : qualifier;
267 theEvent.ie_EventAddress = (APTR) (message->IAddress);
269 length = RawKeyConvert(&theEvent, (char *)buffer,
270 (long) sizeof(buffer), NULL);
272 if (length == 1) { /* Plain ASCII character */
275 * If iflags.num_pad is set, movement is by 4286.
276 * If not set, translate 4286 into hjkl.
277 * This way, the numeric pad can /always/ be used
278 * for moving, though best results are when it is off.
281 if (!iflags.num_pad && numeric_pad && length >= '1' && length <= '9') {
284 length = ctrl_numpad[length];
286 length = shift_numpad[length];
288 length = numpad[length];
292 /* Kludge to allow altmeta on eg. scandinavian keymap (# == shift+alt+3)
293 and prevent it from interfering with # command (M-#) */
294 if (length == ('#'|0x80))
296 if (alt && flags.altmeta)
299 } /* else shift, ctrl, alt, amiga, F-key, shift-tab, etc */
300 else if( length > 1 )
304 if( length == 3 && buffer[ 0 ] == 155 && buffer[ 2 ] == 126 )
307 switch( buffer[ 1 ] )
309 case 53: mxsize = mysize = 8; break;
310 case 54: mxsize = mysize = 16; break;
311 case 55: mxsize = mysize = 24; break;
312 case 56: mxsize = mysize = 32; break;
313 case 57: mxsize = mysize = 48; break;
314 default: got = 0; break;
322 CO = (w->Width-w->BorderLeft-w->BorderRight)/mxsize;
323 LI = (w->Height-w->BorderTop-w->BorderBottom)/mysize;
324 clipxmax = CO + clipx;
325 clipymax = LI + clipy;
326 if( CO < COLNO || LI < ROWNO )
328 amii_cliparound( u.ux, u.uy );
339 /*BufferQueueChar( 'R'-64 );*/
343 printf( "Unrecognized key: %d ", (int)buffer[0]);
344 for( i = 1; i < length; ++i )
345 printf( "%d ", (int)buffer[i]);
352 * Process an incoming IntuiMessage.
353 * It would certainly look nicer if this could be done using a
354 * PA_SOFTINT message port, but we cannot call RawKeyConvert()
355 * during a software interrupt.
356 * Anyway, amikbhit()/kbhit() is called often enough, and usually gets
357 * ahead of input demands, when the user types ahead.
360 static void ProcessMessage(message)
361 register struct IntuiMessage *message;
366 static int skip_mouse=0; /* need to ignore next mouse event on
367 * a window activation */
368 struct Window *w = message->IDCMPWindow;
370 switch(message->Class) {
372 if( alwaysinvent && WIN_INVEN != WIN_ERR &&
373 w == amii_wins[ WIN_INVEN ]->win )
375 cnt = DoMenuScroll( WIN_INVEN, 0, PICK_NONE, &mip );
377 else if( scrollmsg && WIN_MESSAGE != WIN_ERR &&
378 w == amii_wins[ WIN_MESSAGE ]->win )
380 cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip );
396 if( !amii_wins[ WIN_MAP ] || w != amii_wins[ WIN_MAP ]->win )
399 if( message->Code == SELECTDOWN )
401 lastevent.type = WEMOUSE;
402 lastevent.un.mouse.x = message->MouseX;
403 lastevent.un.mouse.y = message->MouseY;
404 /* With shift equals RUN */
405 lastevent.un.mouse.qual = (message->Qualifier &
406 (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) != 0;
414 struct MenuItem *item;
416 thismenu = message->Code;
417 while (thismenu != MENUNULL)
419 item = ItemAddress(MenuStrip, (ULONG) thismenu);
420 if (KbdBuffered < KBDBUFFER)
421 BufferQueueChar((char)(GTMENUITEM_USERDATA(item)));
422 thismenu = item->NextSelect;
430 && amii_wins[ WIN_MESSAGE ]
431 && w == amii_wins[ WIN_MESSAGE ]->win
433 cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip );
439 if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win )
441 dismiss_nhwindow( WIN_INVEN );
444 && ( WIN_OVER != WIN_ERR && w == amii_wins[ WIN_OVER ]->win )
446 destroy_nhwindow( WIN_OVER );
452 if (!(message->Code & IECODE_UP_PREFIX)){
453 /* May queue multiple characters
454 * but doesn't do that yet...
456 if( ( c = ConvertKey(message) ) > 0 )
457 BufferQueueChar( c );
462 if( WIN_MESSAGE != WIN_ERR && w == amii_wins[ WIN_MESSAGE ]->win )
464 cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip );
466 else if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win )
468 cnt = DoMenuScroll( WIN_INVEN, 0, PICK_NONE, &mip );
473 if( WIN_MESSAGE != WIN_ERR && w == amii_wins[ WIN_MESSAGE ]->win )
477 /* Make sure that new size is honored for good. */
478 SetAPen( w->RPort, amii_msgBPen );
479 SetBPen( w->RPort, amii_msgBPen );
480 SetDrMd( w->RPort, JAM2 );
481 RectFill( w->RPort, w->BorderLeft, w->BorderTop,
482 w->Width - w->BorderRight-1,
483 w->Height - w->BorderBottom-1 );
485 ReDisplayData( WIN_MESSAGE );
487 else if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win )
489 ReDisplayData( WIN_INVEN );
491 else if( WINVERS_AMIV
492 && ( WIN_OVER != WIN_ERR && w == amii_wins[ WIN_OVER ]->win )
494 BufferQueueChar( 'R'-64 );
496 else if( WIN_MAP != WIN_ERR && w == amii_wins[ WIN_MAP ]->win )
499 CO = (w->Width-w->BorderLeft-w->BorderRight)/mxsize;
500 LI = (w->Height-w->BorderTop-w->BorderBottom)/mysize;
501 clipxmax = CO + clipx;
502 clipymax = LI + clipy;
503 if( CO < COLNO || LI < ROWNO )
505 amii_cliparound( u.ux, u.uy );
512 BufferQueueChar( 'R'-64 );
517 ReplyMsg((struct Message *) message);
519 switch(delayed_key_action){
521 amii_destroy_nhwindow( WIN_OVER );
523 delayed_key_action = NoAction;
529 #endif /* AMII_GRAPHICS */
531 * Get all incoming messages and fill up the keyboard buffer,
532 * thus allowing Intuition to (maybe) free up the IntuiMessages.
533 * Return when no more messages left, or keyboard buffer half full.
534 * We need to do this since there is no one-to-one correspondence
535 * between characters and incoming messages.
538 #if defined(TTY_GRAPHICS) && !defined(AMII_GRAPHICS)
548 /* a kludge to defuse the mess in allmain.c */
549 /* I hope this is the right approach */
550 if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows)return 0;
564 register struct IntuiMessage *message;
565 while( KbdBuffered < KBDBUFFER / 2 )
568 message = (struct IntuiMessage *) GetFMsg(HackPort);
570 message = (struct IntuiMessage *) GetMsg(HackPort);
574 ProcessMessage(message);
575 if( lastevent.type != WEUNK && lastevent.type != WEKEY )
581 return ( lastevent.type == WEUNK ) ? KbdBuffered : -1;
585 * Get a character from the keyboard buffer, waiting if not available.
586 * Ignore other kinds of events that happen in the mean time.
591 while ((lastevent.type = WEUNK), amikbhit() <= 0) {
594 return BufferGetchar();
597 WETYPE WindowGetevent()
599 lastevent.type = WEUNK;
600 while (amikbhit() == 0)
607 lastevent.type = WEKEY;
608 lastevent.un.key = BufferGetchar();
610 return( lastevent.type );
614 * Clean up everything. But before we do, ask the user to hit return
615 * when there is something that s/he should read.
620 register struct IntuiMessage *msg;
622 /* Close things up */
629 if (ConsoleIO.io_Device)
630 CloseDevice( (struct IORequest *)&ConsoleIO );
631 ConsoleIO.io_Device = 0;
633 if( ConsoleIO.io_Message.mn_ReplyPort )
634 DeleteMsgPort( ConsoleIO.io_Message.mn_ReplyPort );
635 ConsoleIO.io_Message.mn_ReplyPort = 0;
637 /* Strip messages before deleting the port */
641 while (msg = (struct IntuiMessage *) GetMsg(HackPort))
642 ReplyMsg((struct Message *) msg);
644 DeleteMsgPort( HackPort );
649 /* Close the screen, under v37 or greater it is a pub screen and there may
650 * be visitors, so check close status and wait till everyone is gone.
654 #ifdef INTUI_NEW_LOOK
655 if( IntuitionBase->LibNode.lib_Version >= 37 )
657 if (MenuStrip) FreeMenus(MenuStrip);
658 if (VisualInfo) FreeVisualInfo(VisualInfo);
659 while( CloseScreen( HackScreen ) == FALSE )
661 struct EasyStruct easy =
663 sizeof( struct EasyStruct ),
666 "Can't Close Screen, Close Visiting Windows",
669 EasyRequest( NULL, &easy, NULL, NULL );
675 CloseScreen(HackScreen);
689 CloseFont( TextsFont );
695 CloseFont( RogueFont );
701 CloseLibrary(DiskfontBase);
707 CloseLibrary((struct Library *)GadToolsBase);
712 CloseLibrary((struct Library *)LayersBase);
717 CloseLibrary((struct Library *)GfxBase);
722 CloseLibrary((struct Library *)IntuitionBase);
723 IntuitionBase = NULL;
728 CloseLibrary((struct Library *)DOSBase);
733 ((struct Process *) FindTask(NULL))->pr_WindowPtr = (APTR) pr_WindowPtr;
738 #endif /* AMII_GRAPHICS */
746 extern char orgdir[];
752 && windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows) {
753 printf("\n\nAbort with alert code %08lx...\n", rc);
757 printf("\n\nAbort with alert code %08lx...\n",rc);
758 /* Alert(rc); this is too severe */
760 # ifdef INTUI_NEW_LOOK
761 if( IntuitionBase->LibNode.lib_Version >= 37 )
763 struct EasyStruct es =
765 sizeof( struct EasyStruct ),
767 "NetHack Panic Request",
768 "NetHack is Aborting with code == 0x%08lx",
769 "Continue Abort|Return to Program|Clean up and exit",
771 fault = EasyRequest( NULL, &es, NULL, (long)rc );
778 /* __emit(0x4afc); */ /* illegal instruction */
779 __emit(0x40fc); /* divide by */
780 __emit(0x0000); /* #0 */
781 /* NOTE: don't move amii_cleanup() above here - */
782 /* it is too likely to kill the system */
783 /* before it can get the SnapShot out, if */
784 /* there is something really wrong. */
788 if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows)
808 /* This routine adapted from AmigaMail IV-37 by Michael Sinz */
809 static struct Message *
811 struct MsgPort *port;
813 struct IntuiMessage *msg,*succ,*succ1;
815 if(msg=(struct IntuiMessage *)GetMsg(port)){
816 if(!flags.amiflush)return((struct Message *)msg);
817 if(msg->Class==RAWKEY){
819 succ=(struct IntuiMessage *)(port->mp_MsgList.lh_Head);
820 while(succ1=(struct IntuiMessage *)
821 (succ->ExecMessage.mn_Node.ln_Succ)){
822 if(succ->Class==RAWKEY){
823 Remove((struct Node *)succ);
824 ReplyMsg((struct Message *)succ);
831 return((struct Message *)msg);
837 struct NewWindow *win;
839 struct NewWindow *nwin;
840 struct Gadget *ngd, *gd, *pgd = NULL;
841 struct PropInfo *pip;
842 struct StringInfo *sip;
844 /* Copy the (Ext)NewWindow structure */
846 nwin = (struct NewWindow *)alloc( sizeof( struct NewWindow ) );
849 /* Now do the gadget list */
851 nwin->FirstGadget = NULL;
852 for( gd = win->FirstGadget; gd; gd = gd->NextGadget )
854 ngd = (struct Gadget *)alloc( sizeof( struct Gadget ) );
856 if( gd->GadgetType == STRGADGET )
858 sip = (struct StringInfo *)alloc( sizeof( struct StringInfo ) );
859 *sip = *((struct StringInfo *)gd->SpecialInfo);
860 sip->Buffer = (UBYTE *) alloc( sip->MaxChars );
862 ngd->SpecialInfo = (APTR)sip;
864 else if( gd->GadgetType == PROPGADGET )
866 pip = (struct PropInfo *)alloc( sizeof( struct PropInfo ) );
867 *pip = *((struct PropInfo *)gd->SpecialInfo);
868 ngd->SpecialInfo = (APTR)pip;
871 pgd->NextGadget = ngd;
873 nwin->FirstGadget = ngd;
875 ngd->NextGadget = NULL;
876 ngd->UserData = (APTR) 0x45f35c3d; // magic cookie for FreeNewWindow()
883 struct NewWindow *win;
885 register struct Gadget *gd, *pgd;
886 register struct StringInfo *sip;
888 for( gd = win->FirstGadget; gd; gd = pgd ) {
889 pgd = gd->NextGadget;
890 if ((ULONG)gd->UserData == 0x45f35c3d) {
891 if( gd->GadgetType == STRGADGET ) {
892 sip = (struct StringInfo *)gd->SpecialInfo;
895 } else if( gd->GadgetType == PROPGADGET ) {
896 free( (struct PropInfo *)gd->SpecialInfo );
907 if (flags.silent) return;
919 amii_number_pad(state)
923 #endif /* AMII_GRAPHICS */
938 void error VA_DECL(const char *, s)