/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/os2/os2_kbdEv.c,v 3.12.2.1 1997/05/12 12:52:34 hohndel Exp $ */
/*
 * (c) Copyright 1994,1996 by Holger Veit
 *			<Holger.Veit@gmd.de>
 * Modified 1996 Sebastien Marineau <marineau@genie.uottawa.ca>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 * HOLGER VEIT  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of Holger Veit shall not be
 * used in advertising or otherwise to promote the sale, use or other dealings
 * in this Software without prior written authorization from Holger Veit.
 *
 */
/* $XConsortium: os2_kbdEv.c /main/10 1996/10/27 11:48:48 kaleb $ */

#define NEED_EVENTS
#include "X.h"
#include "Xproto.h"
#include "misc.h"
#include "inputstr.h"
#include "scrnintstr.h"

#define I_NEED_OS2_H
#define INCL_KBD
#define INCL_DOSMONITORS
#define INCL_WINSWITCHLIST
#define INCL_DOSQUEUES
#undef RT_FONT	/* must discard this */
#include "xf86.h"
#include "xf86Priv.h"
#include "xf86_OSlib.h"
#include "atKeynames.h"

/* Attention! these lines copied from ../../common/xf86Events.c */
#define XE_POINTER 1
#define XE_KEYBOARD 2

#ifdef XKB
extern Bool noXkbExtension;
#endif

#ifdef XTESTEXT1

#define	XTestSERVER_SIDE
#include "xtestext1.h"
extern short xtest_mousex;
extern short xtest_mousey;
extern int   on_steal_input;          
extern Bool  XTestStealKeyData();
extern void  XTestStealMotionData();

#ifdef XINPUT
#define ENQUEUE(ev, code, direction, dev_type) \
  (ev)->u.u.detail = (code); \
  (ev)->u.u.type   = (direction); \
  if (!on_steal_input ||  \
      XTestStealKeyData((ev)->u.u.detail, (ev)->u.u.type, dev_type, \
                        xtest_mousex, xtest_mousey)) \
  xf86eqEnqueue((ev))
#else
#define ENQUEUE(ev, code, direction, dev_type) \
  (ev)->u.u.detail = (code); \
  (ev)->u.u.type   = (direction); \
  if (!on_steal_input ||  \
      XTestStealKeyData((ev)->u.u.detail, (ev)->u.u.type, dev_type, \
			xtest_mousex, xtest_mousey)) \
  mieqEnqueue((ev))
#endif

#define MOVEPOINTER(dx, dy, time) \
  if (on_steal_input) \
    XTestStealMotionData(dx, dy, XE_POINTER, xtest_mousex, xtest_mousey); \
  miPointerDeltaCursor (dx, dy, time)

#else /* ! XTESTEXT1 */

#ifdef XINPUT
#define ENQUEUE(ev, code, direction, dev_type) \
  (ev)->u.u.detail = (code); \
  (ev)->u.u.type   = (direction); \
  xf86eqEnqueue((ev))
#else
#define ENQUEUE(ev, code, direction, dev_type) \
  (ev)->u.u.detail = (code); \
  (ev)->u.u.type   = (direction); \
  mieqEnqueue((ev))
#endif
#define MOVEPOINTER(dx, dy, time) \
  miPointerDeltaCursor (dx, dy, time)

#endif
/* end of include */

HQUEUE hKbdQueue;
HEV hKbdSem;
int last_status;
int lastStatus;
int lastShiftState;
extern BOOL SwitchedToWPS;

void os2PostKbdEvent();

int os2KbdQueueQuery()
{
    APIRET rc;
    ULONG numElements,postCount;
    
	rc=DosQueryQueue(hKbdQueue,&numElements);
	if((numElements!=0)) return(0); /* We have something in queue */

    DosResetEventSem(hKbdSem,&postCount);
    return (1);
}


void xf86KbdEvents()
{
    KBDKEYINFO keybuf;
    APIRET rc;
    ULONG numElements;
    REQUESTDATA requestData;
    ULONG dataLength,postCount;
    PVOID junkPointer;
    BYTE elemPriority;
    int scan,down;
    static int last;
    USHORT ModState;
    int i;

     while((rc=DosReadQueue(hKbdQueue,&requestData,&dataLength,&junkPointer,
                 0L,1L,&elemPriority,hKbdSem))==0){

        /*ErrorF("Got queue element. rc=%d, data=%d, scancode =%d,up=%d, ddflag %d\n",rc,requestData.ulData,
                (requestData.ulData&0x7F00)>>8,requestData.ulData&0x8000,requestData.ulData>>16);*/

        scan=(requestData.ulData&0x7F00)>>8;

	/* the separate cursor keys return 0xe0/scan */
	if ((requestData.ulData & 0x3F0000)==0x20000) scan=0;
        if (requestData.ulData & 0x800000) {
           switch (scan) {

/* BUG ALERT: IBM has in its keyboard driver a 122 key keyboard, which
 * uses the "server generated scancodes" from atKeynames.h as real scan codes.
 * We wait until some poor guy with such a keyboard will break the whole
 * card house though...
 */
	    case KEY_KP_7: scan = KEY_Home; break;	
	    case KEY_KP_8: scan = KEY_Up;  break;	
	    case KEY_KP_9: scan = KEY_PgUp; break;	
	    case KEY_KP_4: scan = KEY_Left; break;	
	    case KEY_KP_5: scan = KEY_Begin; break;	
	    case KEY_KP_6: scan = KEY_Right; break;	
	    case KEY_KP_1: scan = KEY_End; break;	
	    case KEY_KP_2: scan = KEY_Down; break;	
	    case KEY_KP_3: scan = KEY_PgDown; break;	
	    case KEY_KP_0: scan = KEY_Insert; break;	
	    case KEY_KP_Decimal: scan = KEY_Delete; break; 
	    case KEY_Enter: scan = KEY_KP_Enter; break;	
	    case KEY_LCtrl: scan = KEY_RCtrl; break;	
	    case KEY_KP_Multiply: scan = KEY_Print; break; 
	    case KEY_Slash: scan = KEY_KP_Divide; break;  
	    case KEY_Alt: scan = KEY_AltLang; break;	
	    case KEY_ScrollLock: scan = KEY_Break; break;  
	    case 0x5b: scan = KEY_LMeta; break;
	    case 0x5c: scan = KEY_RMeta; break;
	    case 0x5d: scan = KEY_Menu; break;
	    default:
		/* virtual shifts: ignore */
		scan = 0; break;
	    }
	}
	
	down = (requestData.ulData&0x8000) ? FALSE : TRUE;
	if(scan!=0) os2PostKbdEvent(scan, down);
    }
    rc = DosResetEventSem(hKbdSem,&postCount);
}

/*
 * xf86PostKbdEvent --
 *	Translate the raw hardware KbdEvent into an XEvent, and tell DIX
 *	about it. Scancode preprocessing and so on is done ...
 *
 *  OS/2 specific xf86PostKbdEvent(key) has been moved from common/xf86Events.c
 *  as some things differ, and I didnīt want to scatter this routine with
 *  ifdefs further (hv).
 */

void os2PostKbdEvent(scanCode, down)
     unsigned scanCode;
     Bool down;
{
    KeyClassRec *keyc = ((DeviceIntPtr)xf86Info.pKeyboard)->key;
    Bool        updateLeds = FALSE;
    Bool        UsePrefix = FALSE;
    Bool        Direction = FALSE;
    xEvent      kevent;
    KeySym      *keysym;
    int         keycode;
    static int  lockkeys = 0;

    if (xf86Info.serverNumLock &&
        ((!xf86Info.numLock && !ModifierDown(ShiftMask)) ||
        (xf86Info.numLock && ModifierDown(ShiftMask)))) {
        /*
         * Hardwired numlock handling ... (Some applications break if they have
         * these keys double defined, like twm)
         */
	switch (scanCode) {
	case KEY_KP_7:        scanCode = KEY_Home;      break;  /* curs home */
	case KEY_KP_8:        scanCode = KEY_Up;        break;  /* curs up */
	case KEY_KP_9:        scanCode = KEY_PgUp;      break;  /* curs pgup */
	case KEY_KP_4:        scanCode = KEY_Left;      break;  /* curs left */
	case KEY_KP_5:        scanCode = KEY_Begin;     break;  /* curs begin */
	case KEY_KP_6:        scanCode = KEY_Right;     break;  /* curs right */
	case KEY_KP_1:        scanCode = KEY_End;       break;  /* curs end */
	case KEY_KP_2:        scanCode = KEY_Down;      break;  /* curs down */
	case KEY_KP_3:        scanCode = KEY_PgDown;    break;  /* curs pgdown */
	case KEY_KP_0:        scanCode = KEY_Insert;    break;  /* curs insert */
	case KEY_KP_Decimal:  scanCode = KEY_Delete;    break;  /* curs delete */
	}
    }

    /*
     * and now get some special keysequences
     */
    if ((ModifierDown(ControlMask | AltMask)) ||
        (ModifierDown(ControlMask | AltLangMask)))
    {
	switch (scanCode) {
	case KEY_BackSpace:
	    if (!xf86Info.dontZap) GiveUp(0);
	return;

	case KEY_KP_Minus:   /* Keypad - */
	    if (!xf86Info.dontZoom) {
		if (down) xf86ZoomViewport(xf86Info.currentScreen, -1);
		return;
	    }
	    break;

	case KEY_KP_Plus:   /* Keypad + */
	    if (!xf86Info.dontZoom) {
		if (down) xf86ZoomViewport(xf86Info.currentScreen,  1);
		return;
	    }
	    break;

        }
    }

    /* CTRL-ESC is std OS/2 hotkey for going back to PM and popping up
     * window list... handled by keyboard driverand PM if you tell it. This is 
     * what we have done, and thus should never detect this key combo */
    if (ModifierDown(ControlMask) && scanCode==KEY_Escape) {
	/* eat it */
	return;	
    } else if (ModifierDown(AltLangMask|AltMask) && scanCode==KEY_Escape) {
	/* same here */
	return;
    }

  /*
   * Now map the scancodes to real X-keycodes ...
   */
  keycode = scanCode + MIN_KEYCODE;
  keysym = (keyc->curKeySyms.map +
	    keyc->curKeySyms.mapWidth * 
	    (keycode - keyc->curKeySyms.minKeyCode));

#ifdef XKB
  if (noXkbExtension) {
#endif
  /*
   * Filter autorepeated caps/num/scroll lock keycodes.
   */
#define CAPSFLAG 0x01
#define NUMFLAG 0x02
#define SCROLLFLAG 0x04
#define MODEFLAG 0x08
  if( down ) {
    switch( keysym[0] ) {
        case XK_Caps_Lock :
          if (lockkeys & CAPSFLAG)
              return;
	  else
	      lockkeys |= CAPSFLAG;
          break;

        case XK_Num_Lock :
          if (lockkeys & NUMFLAG)
              return;
	  else
	      lockkeys |= NUMFLAG;
          break;

        case XK_Scroll_Lock :
          if (lockkeys & SCROLLFLAG)
              return;
	  else
	      lockkeys |= SCROLLFLAG;
          break;
    }
    if (keysym[1] == XF86XK_ModeLock)
    {
      if (lockkeys & MODEFLAG)
          return;
      else
          lockkeys |= MODEFLAG;
    }
      
  }
  else {
    switch( keysym[0] ) {
        case XK_Caps_Lock :
            lockkeys &= ~CAPSFLAG;
            break;

        case XK_Num_Lock :
            lockkeys &= ~NUMFLAG;
            break;

        case XK_Scroll_Lock :
            lockkeys &= ~SCROLLFLAG;
            break;
    }
    if (keysym[1] == XF86XK_ModeLock)
      lockkeys &= ~MODEFLAG;
  }

  /*
   * LockKey special handling:
   * ignore releases, toggle on & off on presses.
   * Don't deal with the Caps_Lock keysym directly, but check the lock modifier
   */
#ifndef PC98
  if (keyc->modifierMap[keycode] & LockMask ||
      keysym[0] == XK_Scroll_Lock ||
      keysym[1] == XF86XK_ModeLock ||
      keysym[0] == XK_Num_Lock)
    {
      Bool flag;

      if (!down) return;
      if (KeyPressed(keycode)) {
	down = !down;
	flag = FALSE;
      }
      else
	flag = TRUE;

      if (keyc->modifierMap[keycode] & LockMask)   xf86Info.capsLock   = flag;
      if (keysym[0] == XK_Num_Lock)    xf86Info.numLock    = flag;
      if (keysym[0] == XK_Scroll_Lock) xf86Info.scrollLock = flag;
      if (keysym[1] == XF86XK_ModeLock)   xf86Info.modeSwitchLock = flag;
      updateLeds = TRUE;
    }
#endif /* not PC98 */	
  /*
   * check for an autorepeat-event
   */
  if ((down && KeyPressed(keycode)) &&
      (xf86Info.autoRepeat != AutoRepeatModeOn || keyc->modifierMap[keycode]))
    return;

  /*
   * normal, non-keypad keys
   */
  if (scanCode < KEY_KP_7 || scanCode > KEY_KP_Decimal) {
    /*
     * magic ALT_L key on AT84 keyboards for multilingual support
     */
    if (xf86Info.kbdType == KB_84 &&
	ModifierDown(AltMask) &&
	keysym[2] != NoSymbol)
      {
	UsePrefix = TRUE;
	Direction = TRUE;
      }
    }
#ifdef XKB /* Warning: got position wrong first time */
   }
#endif
   xf86Info.lastEventTime = kevent.u.keyButtonPointer.time = GetTimeInMillis();

  /*
   * And now send these prefixes ...
   * NOTE: There cannot be multiple Mode_Switch keys !!!!
   */
  if (UsePrefix)
    {
      ENQUEUE(&kevent,
	      keyc->modifierKeyMap[keyc->maxKeysPerModifier*7],
	      (Direction ? KeyPress : KeyRelease),
	      XE_KEYBOARD);
      ENQUEUE(&kevent, keycode, (down ? KeyPress : KeyRelease), XE_KEYBOARD);
      ENQUEUE(&kevent,
	      keyc->modifierKeyMap[keyc->maxKeysPerModifier*7],
	      (Direction ? KeyRelease : KeyPress),
	      XE_KEYBOARD);
    }
  else 
    {
#ifdef XFreeDGA
      if (((ScrnInfoPtr)(xf86Info.currentScreen->devPrivates[xf86ScreenIndex].ptr))->directMode&XF86DGADirectKeyb) {
          XF86DirectVideoKeyEvent(&kevent, keycode, (down ? KeyPress : KeyRelease));
      } else
#endif
    {
      ENQUEUE(&kevent, keycode, (down ? KeyPress : KeyRelease), XE_KEYBOARD);
    }
  }

  if (updateLeds) xf86KbdLeds();
}


#pragma pack(1)
struct KeyPacket
  {
    unsigned short mnflags;
    KBDKEYINFO cp;
    unsigned short ddflags;
  };
#pragma pack()

/* The next function runs as a thread. It registers a monitor on the kbd
 * driver, and uses that to get keystrokes. This is because the standard
 * OS/2 keyboard driver does not send keyboard release events. A queue
 * is used to communicate with the main thread to send keystrokes */

void os2KbdMonitorThread(arg)
void *arg;
{
        struct KeyPacket packet;
        APIRET rc;
        USHORT length,print_flag;
        ULONG queueParam;
        HMONITOR hKbdMonitor;
        MONIN monInbuf;
        MONOUT monOutbuf;
        char queueName[128];


        /* monInbuf=(MONIN *)_tmalloc(2*sizeof(MONIN));
        if(monInbuf==NULL){
            ErrorF("xf86-OS/2: Could not allocate memory in kbd monitor thread!\n");
            exit(1);
            }
        monOutbuf=(MONOUT *) &monInbuf[1]; */

        monInbuf.cb=sizeof(MONIN);
        monOutbuf.cb=sizeof(MONOUT);

        rc=DosMonOpen("KBD$",&hKbdMonitor);
        ErrorF("xf86-OS/2: Opened kbd monitor, rc=%d\n",rc);
 	rc=DosMonReg(hKbdMonitor,(PBYTE)&monInbuf,(PBYTE)&monOutbuf,(USHORT)2,(USHORT)-1);
        ErrorF("xf86-OS/2: Kbd monitor registered, rc=%d\n",rc);
	if(rc){
		DosMonClose(hKbdMonitor); exit(1);
		}
        sprintf(queueName,"\\QUEUES\\XF86KBD\\%d",getpid());
        rc=DosCreateQueue(&hKbdQueue,0L,queueName);
        ErrorF("xf86-OS/2: Kbd Queue created, rc=%d\n",rc);
        rc=DosPurgeQueue(hKbdQueue);

        while(1){
           length=sizeof(packet);
              rc=DosMonRead((PBYTE)&monInbuf,0,(PBYTE)&packet,&length);
                if(rc){
                        ErrorF("DosMonRead returned bad RC! rc=%d\n",rc);
                        DosMonClose(hKbdMonitor); exit(1);
                        }
                queueParam=packet.mnflags+(packet.ddflags<<16);
                if(packet.mnflags&0x7F00) rc=DosWriteQueue(hKbdQueue,queueParam,0L,NULL,0L);
                /*ErrorF("xf86-OS/2: wrote a char to queue, rc=%d\n",rc); */
                print_flag=packet.ddflags & 0x1F;
 
                /*ErrorF("Kbd Monitor: Key press %d, scan code %d, ddflags %d\n",
                        packet.mnflags&0x8000,(packet.mnflags&0x7F00)>>8,packet.ddflags); */
/* This line will swallow print-screen keypresses */
                if(print_flag == 0x13 || print_flag == 0x14 || print_flag == 0x15 || print_flag == 0x16){ rc = 0; }
                else { rc=DosMonWrite((PBYTE)&monOutbuf,(PBYTE)&packet,length); }
                if(rc) {
                        ErrorF("DosMonWrite returned bad RC! rc=%d\n",rc);
                        DosMonClose(hKbdMonitor); exit(1);
                        }
                }
        rc=DosCloseQueue(hKbdQueue);
        rc=DosMonClose(hKbdMonitor);
}

void os2KbdBitBucketThread(arg)
void *arg;
{
   KBDKEYINFO key;
        while(1){
        if(xf86Info.consoleFd!=-1){
                KbdCharIn(&key,1,xf86Info.consoleFd);
                usleep(100000);
                }
        else {
                usleep(500000);
                }
        }
}
