#include "ikterm.h"

#include "../ckocon.h"

extern "C" {
#include "ikui.h"
extern enum markmodes markmodeflag[] ;
extern vscrn_t vscrn[VNUM]; /* = {0,0,0,{0,0,0},0,-1,-1,-1,-1,{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},0,0}; */
extern int inecho;          /* do we echo script INPUT output? */
extern bool in_smooth_scroll;
extern bool smooth_scroll_upwards;
extern int smooth_scroll_top, smooth_scroll_bottom;
extern int smooth_scroll_left, smooth_scroll_right;
extern videoline s_scroll_backup_line;
extern int priority ;
extern cell_video_attr_t defaultattribute ;
extern int cursoron[], cursorena[],scrollflag[], scrollstatus[], flipscrnflag[];
extern int tt_update, tt_rows[], tt_cols[], tt_font, tt_roll[],
           tt_cursor, scrninitialized[], ttyfd, viewonly, carrier, network,
           tt_scrsize[], tt_modechg, pheight, pwidth, tt_status[], screenon, 
           decssdt, tt_url_hilite, tt_url_hilite_attr, tt_type_mode, ttnum;
extern bool decssdt_override;
extern TID tidTermScrnUpd ;
extern cell_video_attr_t     colorstatus;
extern cell_video_attr_t     colorselect;
extern BYTE vmode;
extern int tcsl;
extern int nt351;

#ifndef NOSCROLLWHEEL
#ifndef GET_WHEEL_DELTA_WPARAM
#define GET_WHEEL_DELTA_WPARAM(wParam)  ((short)HIWORD(wParam))
#endif /* GET_WHEEL_DELTA_WPARAM */
#endif /* NOSCROLLWHEEL */

extern void
win32KeyEvent( int mode, KEY_EVENT_RECORD key );
#ifdef OS2MOUSE
extern void 
win32MouseEvent( int mode, MOUSE_EVENT_RECORD r );
#endif /* OS2MOUSE */

}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
void getDimensions( BYTE vnum, int* w, int* h )
{
    vnum = vmode;

	// We use the actual struct members instead of the access methods
	// because the access methods take into account the decsasd state.
	// which could result in the wrong value be returned.
    *w = VscrnGetWidth(vnum);
    *h = VscrnGetDisplayHeight(vnum);
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
long getMaxDim()
{
    return MAXTERMSIZE;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
void getMaxSizes( int* column, int* row )
{
    *column = MAXTERMCOL;
    *row    = MAXTERMROW;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
IKTerm::IKTerm( BYTE whichbuffer, K_CLIENT_PAINT* clipaint )
    : vnum( whichbuffer )
    , kcp( clipaint ), mouseCaptured(0), vt_char_attrs(0), cell_attrs(0)
{
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
IKTerm::~IKTerm()
{
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
#define URLMINCNT 4096
#define EXCLUSIVE 1

BOOL IKTerm::getDrawInfo() {
    return getDrawInfo( vmode );
}
BOOL IKTerm::getDrawInfo(BYTE vscrn_number) {
    return getDrawInfo( vscrn_number, FALSE );
}

bool IKTerm::getSmoothScrollDrawInfo() {
    return getDrawInfo( vmode, TRUE );
}

BOOL IKTerm::getDrawInfo(BYTE vscrn_number, bool smoothScroll)
{
	vnum = vscrn_number;
#ifdef EXCLUSIVE
    /* Wait for exclusive access to the screen */
    if ( RequestVscrnMutex( vnum, 200 ) ) {
        debug(F101,"IKTerm::getDrawInfo unable to acquire Mutex","",vnum);
        return FALSE;
    }
    debug(F101,"IKTerm::getDrawInfo has Mutex","",vnum);
#endif /* EXCLUSIVE */

    xs = VscrnGetWidth( vnum );

    c = 0;
    cursor_offset = 0;

    incnt = 0;

    vbuf = &(vscrn[vnum]);
    vscrn_page_t *page = &vscrn_view_page(vnum);
    if ( !vscrn_view_page_valid(vnum) ) {
        ReleaseVscrnMutex(vnum) ;
        return FALSE;
    }

    // These variables are...
    //      xs - x size? Width of the area to be rendered
    //      xho - x horizontal offset? For horizontal scrolling
    //      ys - Y size? Height of the area to be rendered
    //      xo - Overlay X coordinate
    //      yo - Overlay Y coordinate
    //  pwidth - Physical screen width in columns.
    // pheight - Physical screen height in rows.
    //
    // pwidth, pheight, tt_modechg, TVC_ENA, etc, are all just a result of much
    // of this class being a copy&paste of the console-mode rendering code
    // adjusted to output to K_CLIENT_PAINT instead of win32 conhost (or an OS/2
    // vio window). In KUI builds pwidth and pheight are never assigned a value
    // and the SET TERM VIDEO-CHANGE command does not assign a value to
    // tt_modechg, so its always TVC_ENA.
    //
    // Someday all of this should change to implement the Windowing Extension.
    // In that case TVC_W95 should be treated as TVC_ENA, and TVC_DIS would mean
    // scrolling the viewport. A new vbuf->hscroll and associated KVerbs would
    // need to be added too. This work will likely impact how the smooth scroll
    // stuff works - it will need to determine if the scrolling area is even in
    // the viewport, etc.

    xs = (tt_modechg == TVC_ENA ) ? vbuf->width : pwidth;
    xho = vbuf->hscroll > (MAXTERMCOL-pwidth) ? 
        (MAXTERMCOL-VscrnGetWidth(vnum)) : vbuf->hscroll ;
    ys = (tt_modechg == TVC_ENA ? vbuf->height : pheight)
        - ((vnum==VTERM && tt_status[vnum] && decssdt!=SSDT_BLANK ||
             vnum != VTERM && tt_status[vnum]) ? 1 : 0 ) ;

    /* Do we have a popup to display? */
    if ( vbuf->popup == NULL) {
        xo = yo = -1 ;
    }
    else {
        xo = (xs - vbuf->popup->width) / 2 ;
        yo = (ys - vbuf->popup->height) / 2 ;
    }

    unsigned long page_top = page->top;
    unsigned long page_scrolltop = page->scrolltop;

    // Scrolling between left and right margins.
    bool lrmm = smooth_scroll_left != -1 && smooth_scroll_right != -1;
    bool tbmm = smooth_scroll_top != -1 && smooth_scroll_bottom != -1;

    // Scrolling between top and bottom margins with the help of a backup line
    bool useBackupLine = (tbmm || lrmm ) && s_scroll_backup_line.cells != NULL;

    if (smoothScroll) {
        // TODO: Take pwidth and pheight into account for situations where the
        //       window is smaller than the buffer. This would primarily affect
        //       scrolling within a region - if the region fits into the
        //       viewport then we don't need to treat it as a scroll region.

        int height = ys;
        int top = smooth_scroll_top < 0 ? 1 : smooth_scroll_top + 1;
        int bot = smooth_scroll_bottom < 0 ? ys : smooth_scroll_bottom + 1;

        // ys is the height of the region we're getting draw info for.
        // Recalculate it taking the top and bottom margins into account.
        ys = bot - top + 1;

        // Then add one to account for the line saved by VscrnScrollPage that
        // we'll slot in at either the top or bottom.
        ys += 1;

        // The top of the area we're rendering will start at the top margin.
        page_top += top - 1;

        // If we're scrolling up, then it gets slotted in at the top so we need
        // to make room there for it. Else it's slotted in at the bottom - the
        // enlarged ys provides room.
        if (smooth_scroll_upwards) {
            page_top -= 1;
        }
        else if (bot == height && useBackupLine) {
            // If we're using the backup line, scrolling in the reverse
            // direction, and the bottom of the scroll region is the screen
            // bottom, then we've got to enlarge ys further, or the backup line
            // gets placed in the wrong place and doesn't render.
            ys += 1;
        }
    } else {
        useBackupLine = lrmm = false;
    }

    textBuffer = kcp->textBuffer;
    attrBuffer = kcp->attrBuffer;
    effectBuffer = kcp->effectBuffer;
    cellAttrBuffer = kcp->cellAttrBuffer;
    lineAttr = kcp->lineAttr;
    unsigned long maxWidth = 0;

    if ( vnum == VTERM && !screenon ) {
        /* Blank out the screen */
        for ( y=0 ; y < ys ; y++ )
            for ( x= 0; x < xs ; x++ ) {
                textBuffer[c++] =' ';
                attrBuffer[c++] = defaultattribute;
                cellAttrBuffer[c++] = '\0';
                effectBuffer[c++] = '\0';
            }
    }
    else if ( markmodeflag[vnum] != marking )
    {
        for ( y = 0 ; y < ys ; y++ )
        {
#ifdef NEW_EXCLUSIVE
            /* Wait for exclusive access to the screen */
            if ( RequestVscrnMutex( vnum, -1 ) )
                return FALSE;
#endif /* NEW_EXCLUSIVE */
            /* Get the next line */
            if (!scrollflag[vnum])
                line = &page->lines[(page_top+y)%page->linecount] ;
            else
                line = &page->lines[(page_scrolltop+y)%page->linecount] ;

            // If we're rendering only the scroll region for a smooth-scroll
            // operation, then the additional line that is scrolling away has
            // been stashed in s_scroll_backup_line by VscrnScrollPage. We need
            // to slot that in now.
            if (useBackupLine) {
                if (smooth_scroll_upwards && y == 0) {
                    // Substitute the top for the backup line
                    line = &s_scroll_backup_line;
                } else if (!smooth_scroll_upwards && y == ys - 1) {
                    // Substitute the bottom for the backup line
                    line = &s_scroll_backup_line;
                }
            }

            lineAttr[y] = line->vt_line_attr;
#ifdef NEW_EXCLUSIVE
            /* Give mutex back */
            ReleaseVscrnMutex(vnum) ;
#endif /* NEW_EXCLUSIVE */

            /* copy the screen data to the buffer */
            if (line->cells != NULL)
            {
                for ( x = 0 ; x < xs ; x++ )
                {
#ifdef NEW_EXCLUSIVE
                    /* Wait for exclusive access to the screen */
                    if ( RequestVscrnMutex( vnum, -1 ) )
                        return FALSE;
#endif /* NEW_EXCLUSIVE */
                    /* Throw away the erased flag - the renderer doesn't care
                     * about it, and it messes with its attribute batching */
                    vt_char_attrs = line->vt_char_attrs[x+xho] & ~VT_CHAR_ATTR_ERASED;
                    cell_attrs = CELL_ATTR_GET(line,x+xho);
#ifdef NEW_EXCLUSIVE
                    /* Give mutex back */
                    ReleaseVscrnMutex(vnum) ;
#endif /* NEW_EXCLUSIVE */

                    /* We should be able to just say cell = line->cells[x] */
                    /* but this doesn't work.  So we go through hell to    */
                    /* compute the offsets properly.                       */
                    viocell cell = line->cells[x+xho] ;

                    if ( vt_char_attrs & VT_CHAR_ATTR_HYPERLINK )
                        vt_char_attrs |= tt_url_hilite_attr;
                    else if ( tt_url_hilite && incnt < URLMINCNT) {
                        if ( IsURLChar(cell.c) && 
                             IsCellPartOfURL(vnum, y, x+xho ) )
                            vt_char_attrs |= tt_url_hilite_attr;
                    }

                    textBuffer[c+x] = cell.c;
                    attrBuffer[c+x] = ComputeColorFromAttr( vnum,
                                cell.video_attr,
                                vt_char_attrs);
                    effectBuffer[c+x] = vt_char_attrs;
                    cellAttrBuffer[c+x] = cell_attrs;
                }
            }
            else
            {
                /* In case we are in the middle of a scroll */
                memset( &(textBuffer[c]), ' ', xs );
                memset( &(effectBuffer[c]), '\0', xs );
                memset( &(cellAttrBuffer[c]), '\0', xs );

                // memset( &(attrBuffer[c]), defaultattribute, xs );
                {
                    cell_video_attr_t* str = &(attrBuffer[c]);
                    for (int i = 0; i < xs; i++) {
                        str[i] = defaultattribute;
                    }
                }

#ifdef VSCRN_DEBUG
                debug(F101,"OUCH!","",(scrollflag?(page_scrolltop+y)
                                    :(page_top+y))%page->linecount);
#endif /* VSCRN_DEBUG */
            }

            /* if there is a popup, display it */
            if ( y >= yo && 
                 vscrn[vnum].popup &&
                 y <= yo+vscrn[vnum].popup->height-1 ) {
                int i ;
                for ( i = 0 ; i < vscrn[vnum].popup->width ; i++ ) {
                    if ( ck_isunicode() ) {
                        textBuffer[c+xo+i] = 
                            vscrn[vnum].popup->c[y-yo][i] < 32 ?
                                (*xl_u[TX_IBMC0GRPH])(vscrn[vnum].popup->c[y-yo][i]) : 
                                    vscrn[vnum].popup->c[y-yo][i] >= 128 ?
                                        (*xl_u[TX_CP437])(vscrn[vnum].popup->c[y-yo][i]) : 
                                            vscrn[vnum].popup->c[y-yo][i] ;
                    }
                    else
                        textBuffer[c+xo+i] = vscrn[vnum].popup->c[y-yo][i];
                    attrBuffer[c+xo+i] = vscrn[vnum].popup->video_attr;
                    effectBuffer[c+xo+i] = '\0';
                    cellAttrBuffer[c+xo+i] = '\0';
                }
            }
            c += xs;        /* advance the pointer in the buffer */
            if ( line->width > maxWidth )
                maxWidth = line->width;
        }
    }
    else
    {  /* were in marking mode */
        for ( y = 0 ; y < ys ; y++ )
        {
#ifdef NEW_EXCLUSIVE
            /* Wait for exclusive access to the screen */
            if ( RequestVscrnMutex( vnum, -1 ) )
                return FALSE;
#endif /* NEW_EXCLUSIVE */
            line = &page->lines[(page_scrolltop+y)%page->linecount] ;
            lineAttr[y] = line->vt_line_attr;
#ifdef NEW_EXCLUSIVE
            /* Give mutex back */
            ReleaseVscrnMutex(vnum) ;
#endif /* NEW_EXCLUSIVE */

            if (line->cells)
            {
                if ( VscrnIsLineMarked(vnum,page_scrolltop+y) )
                {
                    for ( x = 0 ; x < xs ; x++ )
                    {
                        if ( line->markshowend != -1 &&
                            x+xho >= line->markbeg &&
                            x+xho <= line->markshowend )
                        {
                            textBuffer[c+x] = line->cells[x+xho].c ;
                            attrBuffer[c+x] = colorselect ;
                            effectBuffer[c+x] = line->vt_char_attrs[x+xho];
                            cellAttrBuffer[c+x] = CELL_ATTR_GET(line,x+xho);
                        }
                        else
                        {
#ifdef NEW_EXCLUSIVE
                            /* Wait for exclusive access to the screen */
                            if ( RequestVscrnMutex( vnum, -1 ) )
                                return FALSE;
#endif /* NEW_EXCLUSIVE */
                            vt_char_attrs = line->vt_char_attrs[x+xho];
                            cell_attrs = CELL_ATTR_GET(line,x+xho);
#ifdef NEW_EXCLUSIVE
                            /* Give mutex back */
                            ReleaseVscrnMutex(vnum) ;
#endif /* NEW_EXCLUSIVE */

                            if ( vt_char_attrs & VT_CHAR_ATTR_HYPERLINK )
                                vt_char_attrs |= tt_url_hilite_attr;
                            else if ( tt_url_hilite && incnt < URLMINCNT) {
                                if ( IsURLChar(line->cells[x+xho].c) &&
                                     IsCellPartOfURL(vnum, y, x+xho ) )
                                    vt_char_attrs |= tt_url_hilite_attr;
                            }

                            textBuffer[c+x] = line->cells[x+xho].c;
                            attrBuffer[c+x] = ComputeColorFromAttr( vnum,
                                    line->cells[x+xho].video_attr,
                                    vt_char_attrs );
                            effectBuffer[c+x] = vt_char_attrs;
                            cellAttrBuffer[c+x] = cell_attrs;
                        }
                    }
                }
                else
                {
                    for ( x = 0 ; x < xs ; x++ )
                    {
#ifdef NEW_EXCLUSIVE
                        /* Wait for exclusive access to the screen */
                        if ( RequestVscrnMutex( vnum, -1 ) )
                            return FALSE;
#endif /* NEW_EXCLUSIVE */
                        vt_char_attrs = line->vt_char_attrs[x+xho];
                        cell_attrs = CELL_ATTR_GET(line,x+xho);
#ifdef NEW_EXCLUSIVE
                        /* Give mutex back */
                        ReleaseVscrnMutex(vnum) ;
#endif /* NEW_EXCLUSIVE */

                        if ( vt_char_attrs & VT_CHAR_ATTR_HYPERLINK )
                            vt_char_attrs |= tt_url_hilite_attr;
                        else if ( tt_url_hilite && incnt < URLMINCNT) {
                            if ( IsURLChar(line->cells[x+xho].c) &&
                                 IsCellPartOfURL(vnum, y, x+xho ) )
                                vt_char_attrs |= tt_url_hilite_attr;
                        }

                        textBuffer[c+x] = line->cells[x+xho].c;
                        attrBuffer[c+x] = ComputeColorFromAttr(vnum,
                            line->cells[x+xho].video_attr,
                            vt_char_attrs);
                        effectBuffer[c+x] = vt_char_attrs;
                        cellAttrBuffer[c+x] = cell_attrs;
                    }
                }
            }
            else
            {
                /* In case we are in the middle of a scroll */
                for ( x = 0 ; x < xs ; x++ )
                {
                    textBuffer[c+x] = ' ';
                    attrBuffer[c+x] = ComputeColorFromAttr(vnum,
                        defaultattribute,0);
                    effectBuffer[c+x] = 0;
                    cellAttrBuffer[c+x] = 0;
                }
            }
            c += xs;
            if ( line->width > maxWidth )
                maxWidth = line->width;
        }
    }
#ifdef EXCLUSIVE
    /* Give mutex back */
    ReleaseVscrnMutex(vnum) ;
    debug(F101,"IKTerm::getDrawInfo releases Mutex","",vnum);
#endif /* EXCLUSIVE */

    /* Status Line Display */
    if ( (vnum == VTERM && tt_status[vnum] && decssdt != SSDT_BLANK ||
         vnum != VTERM && tt_status[vnum] || decssdt_override) &&
         !useBackupLine && !lrmm)
    {
        if ( vnum == VTERM && decssdt == SSDT_HOST_WRITABLE && tt_status[vnum] == 1
                  && !decssdt_override && !scrollflag[vnum]) {
            line = &vscrn_view_page(VSTATUS).lines[0] ;
            for ( x = 0 ; x < xs ; x++ ) {
                textBuffer[c+x] = line->cells[x].c;
                attrBuffer[c+x] = ComputeColorFromAttr(vnum,
                                                        line->cells[x].video_attr,
                                                        line->vt_char_attrs[x]);
                effectBuffer[c+x] = line->vt_char_attrs[x];
                cellAttrBuffer[c+x] = CELL_ATTR_GET(line,x);
            }
            c += xs ;
        }
        else {/* SSDT_INDICATOR - Kermit Status Line */
            char * status = line25(vnum);

            for ( x = 0 ; x < xs ; x++ ) {
                if ( ck_isunicode() ) {
                    textBuffer[c] = status[x] < 32 ? 
                        (*xl_u[TX_IBMC0GRPH])(status[x]) : 
                            status[x] >= 128 ? 
                                (*xl_u[TX_CP437])(status[x]) : 
                                    status[x] ;
                }
                else
                    textBuffer[c] = status[x] ;
                attrBuffer[c] = colorstatus ;
                effectBuffer[c] = '\0';
                cellAttrBuffer[c] = '\0';
                c++ ;
            }
        }
    }

    getCursorPos();

    kcp->beg = page->beg;
    kcp->top = page->top;
    kcp->scrolltop = page->scrolltop;
    kcp->end = page->end;
    kcp->maxWidth = maxWidth;
    kcp->len = c;
    kcp->page = vscrn[vnum].view_page;
    kcp->page_length = page->linecount;
    kcp->height = ys + tt_status[vnum];
    return TRUE;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
BOOL IKTerm::getCursorPos()
{
    vnum = vmode;
    /* Wait for exclusive access to the screen */
    if ( RequestVscrnMutex( vnum, -1 ) )
        return FALSE;

    vbuf = &(vscrn[vnum]);
    vscrn_page_t *page = &vscrn_view_page(vmode);
	int cursor_page = vbuf->cursor.p;
	int cursor_x = vbuf->cursor.x;
	int cursor_y = vbuf->cursor.y;
	int page_top = page->top;
	int page_linecount = page->linecount;
	int page_scrolltop = page->scrolltop;
	ReleaseVscrnMutex(vnum) ;

    char buf[30];
    if (cursor_page == 0) {
        ckmakmsg(buf,30,ckitoa(cursor_x+1),", ", ckitoa(cursor_y+1),NULL);
    } else if (!on_alternate_buffer(vnum)) {
        ckmakxmsg(buf,30,ckitoa(cursor_page+1),"(",
                  ckitoa(cursor_x+1),", ", ckitoa(cursor_y+1), ")",
                  NULL,NULL,NULL,NULL,NULL,NULL);
    } else { /* cursor is on the alternate screen */
        ckmakxmsg(buf,30,"A(",
                  ckitoa(cursor_x+1),", ", ckitoa(cursor_y+1), ")",
                  NULL,NULL,NULL,NULL,NULL,NULL,NULL);
    }
    KuiSetTerminalStatusText(STATUS_CURPOS, buf);

    /* only calculated an offset if Roll mode is INSERT */
    if( scrollstatus[vnum] && tt_roll[vnum] && markmodeflag[vnum] == notmarking ) {
        cursor_offset = (page_top + page_linecount - page_scrolltop)
                        % page_linecount;
    }
    else {
        cursor_offset = 0;
    }

    ys = VscrnGetHeightEx( vnum, FALSE );
    if( (!cursorena[vnum] || !cursor_on_visible_page(vnum)
            || cursor_y + cursor_offset >= ys -(tt_status[vnum]?1:0))
            && markmodeflag[vnum] == notmarking ) {
        kcp->cursorVisible = FALSE;
    }
    else {
        kcp->cursorPt.x = cursor_x;
        kcp->cursorPt.y = cursor_y + cursor_offset;
        kcp->cursorVisible = TRUE;
    }

    return kcp->cursorVisible;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
DWORD IKTerm::getControlKeyState()
{
    SHORT keystate;
    DWORD dwControlKeyState = 0x00000000;

    keystate = GetKeyState( VK_SHIFT );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= SHIFT_PRESSED;

    keystate = GetKeyState( VK_LSHIFT );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= SHIFT_PRESSED;

    keystate = GetKeyState( VK_RSHIFT );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= SHIFT_PRESSED;

    keystate = GetKeyState( VK_LCONTROL );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= LEFT_CTRL_PRESSED;

    keystate = GetKeyState( VK_RCONTROL );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= RIGHT_CTRL_PRESSED;

    if ( !(dwControlKeyState & LEFT_CTRL_PRESSED) &&
         !(dwControlKeyState & RIGHT_CTRL_PRESSED) ) {
        keystate = GetKeyState( VK_CONTROL );
        if( HIBYTE( keystate ) )
            dwControlKeyState |= LEFT_CTRL_PRESSED;
    }

    keystate = GetKeyState( VK_LMENU );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= LEFT_ALT_PRESSED;

    keystate = GetKeyState( VK_RMENU );
    if( HIBYTE( keystate ) )
        dwControlKeyState |= RIGHT_ALT_PRESSED;

    if ( !(dwControlKeyState & LEFT_ALT_PRESSED) &&
         !(dwControlKeyState & RIGHT_ALT_PRESSED) ) {
        keystate = GetKeyState( VK_MENU );
        if( HIBYTE( keystate ) )
            dwControlKeyState |= LEFT_ALT_PRESSED;
    }

    keystate = GetKeyState( VK_SCROLL );
    if( LOBYTE( keystate ) )
        dwControlKeyState |= SCROLLLOCK_ON;

    keystate = GetKeyState( VK_NUMLOCK );
    if( LOBYTE( keystate ) )
        dwControlKeyState |= NUMLOCK_ON;

    keystate = GetKeyState( VK_CAPITAL );
    if( LOBYTE( keystate ) )
        dwControlKeyState |= CAPSLOCK_ON;

    return dwControlKeyState;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
BOOL IKTerm::newKeyboardEvent( UINT chCharCode, LONG lKeyData, UINT keyDown, UINT sysKey )
{
    int keycount = 1 ;
    WORD wbuf[8] ;
    CHAR buf[8] ;
    static unsigned char keystate[256] ;
    static int (__stdcall *toUnicodeEx)(UINT,UINT,CONST PBYTE,LPWSTR,int,UINT,HKL)=NULL;
    static int (__stdcall *toUnicode)(UINT,UINT,CONST PBYTE,LPWSTR,int,UINT)=NULL;
    static int toUnicodeLoad = 0;

    vnum = vmode;

    memset( buf, '\0', sizeof(buf) );
    memset( &inpEvt, '\0', sizeof(INPUT_RECORD) );
    inpEvt.EventType = KEY_EVENT;
    inpEvt.Event.KeyEvent.bKeyDown = keyDown;
    inpEvt.Event.KeyEvent.wRepeatCount = (WORD)(lKeyData & 0x0000FFFF);

    long scancode = ( lKeyData & 0x00FF0000 ) >> 16;
    inpEvt.Event.KeyEvent.wVirtualScanCode = (UINT) scancode;

    inpEvt.Event.KeyEvent.wVirtualKeyCode = chCharCode;

    inpEvt.Event.KeyEvent.dwControlKeyState = getControlKeyState();
    if( lKeyData & 0x01000000 )
        inpEvt.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;

    GetKeyboardState(keystate);

#ifdef COMMENT      /* COMMENT A */
    if ( 0 ) {
        inpEvt.Event.KeyEvent.uChar.AsciiChar = 0;
#ifndef NOTERM
        if ( ISVTNT(tt_type_mode) &&
             ttnum != -1 
#ifndef NOLOCAL
             && IsConnectMode() 
#endif /* NOLOCAL */
             )
            ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
        else
#endif /* NOTERM */
        win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
    } else {
#endif /* COMMENT */      /* COMMENT A */
        if ( !isWin95() ) {
            int i;
            if ( !toUnicodeLoad ) {
                HINSTANCE hUSER32 = LoadLibrary("USER32");
                toUnicodeEx = (int (__stdcall *)(UINT,UINT,CONST PBYTE,LPWSTR,int,UINT,HKL))
                    GetProcAddress( hUSER32, "ToUnicodeEx" );
                toUnicode = (int (__stdcall *)(UINT,UINT,CONST PBYTE,LPWSTR,int,UINT))
                    GetProcAddress( hUSER32, "ToUnicode" );
                toUnicodeLoad = 1;
                debug(F101,"ToUnicodeEx","",toUnicodeEx);
                debug(F101,"ToUnicode","",toUnicode);
            }

#ifndef CKT_NT35_OR_31
            if ( nt351 )
#endif
                keycount = toUnicode( inpEvt.Event.KeyEvent.wVirtualKeyCode,
                        inpEvt.Event.KeyEvent.wVirtualScanCode | (keyDown ? 0x00 : 0x8000),
                        keystate,
                        (LPWSTR)wbuf,
                        8,
                        FALSE );
#ifndef CKT_NT35_OR_31
            else
                keycount = toUnicodeEx( inpEvt.Event.KeyEvent.wVirtualKeyCode,
                        inpEvt.Event.KeyEvent.wVirtualScanCode | (keyDown ? 0x00 : 0x8000),
                        keystate,
                        (LPWSTR)wbuf,
                        8,
                        FALSE,
                        GetKeyboardLayout(0) );
#endif /* CKT_NT35_OR_31 */
            for ( i=0;i<keycount;i++ )
                buf[i] = xl_tx[tcsl](wbuf[i]);
        }
#ifndef CKT_NT35_OR_31
        else {
            keycount = ToAsciiEx( inpEvt.Event.KeyEvent.wVirtualKeyCode,
                        inpEvt.Event.KeyEvent.wVirtualScanCode | (keyDown ? 0x00 : 0x8000),
                        keystate,
                        (LPWORD)buf,
                        FALSE,
                        GetKeyboardLayout(0) );                         
        }
#endif /* CKT_NT35_OR_31 */

#ifdef COMMENT
        printf("\n");
        printf("kui KeyDown               0x%X\n",keyDown);
        printf("kui RepeatCount           0x%X\n",inpEvt.Event.KeyEvent.wRepeatCount);
        printf("kui wParam                0x%X\n",chCharCode);
        printf("kui keycount              0x%X\n",keycount);
        if ( keycount > 0 )
            printf("kui buf[0]                0x%X\n",buf[0]);
        if ( keycount > 1 )
            printf("kui buf[1]                0x%X\n",buf[1]);
        printf("\n");
#endif /* COMMENT */

        if ( keycount == 1 ) {
            inpEvt.Event.KeyEvent.uChar.AsciiChar = buf[0];
#ifndef NOTERM
            if ( ISVTNT(tt_type_mode) &&
                 ttnum != -1 
#ifndef NOLOCAL
                 && IsConnectMode() 
#endif /* NOLOCAL */
                 )
                ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
            else
#endif /* NOTERM */
            win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
        } else if ( keycount == 0 ) {
            inpEvt.Event.KeyEvent.uChar.AsciiChar = 0;
#ifndef NOTERM
            if ( ISVTNT(tt_type_mode) &&
                 ttnum != -1 
#ifndef NOLOCAL
                 && IsConnectMode() 
#endif /* NOLOCAL */
                 )
                ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
            else
#endif /* NOTERM */
            win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
        } else if ( keycount < 0 ) {
            inpEvt.Event.KeyEvent.wVirtualScanCode = 0;
            inpEvt.Event.KeyEvent.uChar.AsciiChar = 0;
#ifndef NOTERM
            if ( ISVTNT(tt_type_mode) &&
                 ttnum != -1 
#ifndef NOLOCAL
                 && IsConnectMode() 
#endif /* NOLOCAL */
                 )
                ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
            else
#endif /* NOTERM */
            win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
        } else if (keycount == 2) {
            inpEvt.Event.KeyEvent.uChar.AsciiChar = buf[0];
#ifndef NOTERM
            if ( ISVTNT(tt_type_mode) &&
                 ttnum != -1 
#ifndef NOLOCAL
                 && IsConnectMode() 
#endif /* NOLOCAL */
                 )
                ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
            else
#endif /* NOTERM */
            win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
            inpEvt.Event.KeyEvent.uChar.AsciiChar = buf[0];
#ifndef NOTERM
            if ( ISVTNT(tt_type_mode) &&
                 ttnum != -1 
#ifndef NOLOCAL
                 && IsConnectMode() 
#endif /* NOLOCAL */
                 )
                ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
            else
#endif /* NOTERM */
            win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
        }
#ifdef COMMENT      /* COMMENT A */
    }
#endif /* COMMENT */      /* COMMENT A */
    return TRUE;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
BOOL IKTerm::keyboardEvent( UINT chCharCode, LONG lKeyData, UINT keyDown )
{
    vnum = vmode;

    memset( &inpEvt, '\0', sizeof(INPUT_RECORD) );
    inpEvt.EventType = KEY_EVENT;
    inpEvt.Event.KeyEvent.bKeyDown = keyDown;
    inpEvt.Event.KeyEvent.wRepeatCount = (WORD)(lKeyData & 0x0000FFFF);
    inpEvt.Event.KeyEvent.uChar.AsciiChar = chCharCode;

    long scancode = ( lKeyData & 0x00FF0000 ) >> 16;
    inpEvt.Event.KeyEvent.wVirtualScanCode = (UINT) scancode;

    inpEvt.Event.KeyEvent.wVirtualKeyCode = MapVirtualKey( scancode, isWin95() ? 1 : 3 );

    inpEvt.Event.KeyEvent.dwControlKeyState = getControlKeyState();
    if( lKeyData & 0x01000000 )
        inpEvt.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;

#ifndef NOTERM
    if ( ISVTNT(tt_type_mode) &&
         ttnum != -1 
#ifndef NOLOCAL
         && IsConnectMode() 
#endif /* NOLOCAL */
         )
        ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
    else
#endif /* NOTERM */
        win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
    return TRUE;
}

/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
BOOL IKTerm::virtkeyEvent( UINT virtkey, LONG lKeyData, UINT keyDown )
{
    vnum = vmode;

    memset( &inpEvt.Event.KeyEvent, '\0', sizeof(KEY_EVENT_RECORD) );

    inpEvt.Event.KeyEvent.bKeyDown = keyDown;
    inpEvt.Event.KeyEvent.wRepeatCount = (WORD)(lKeyData & 0x0000FFFF);
    inpEvt.Event.KeyEvent.uChar.AsciiChar =  virtkey;

    long scancode = ( lKeyData & 0x00FF0000 ) >> 16;
    inpEvt.Event.KeyEvent.wVirtualScanCode = (UINT) scancode;

    inpEvt.Event.KeyEvent.wVirtualKeyCode = MapVirtualKey( scancode, isWin95() ? 1 : 3 );

    inpEvt.Event.KeyEvent.dwControlKeyState = getControlKeyState();
    if( lKeyData & 0x01000000 )
        inpEvt.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;

#ifndef NOTERM
    if ( ISVTNT(tt_type_mode) &&
         ttnum != -1 
#ifndef NOLOCAL
         && IsConnectMode() 
#endif /* NOLOCAL */
         )
        ttol((unsigned char *)&inpEvt,sizeof(INPUT_RECORD));
    else
#endif /* NOTERM */
        win32KeyEvent( vnum, inpEvt.Event.KeyEvent );
    return TRUE;
}
/*------------------------------------------------------------------------
------------------------------------------------------------------------*/
void IKTerm::mouseEvent( HWND hwnd, UINT msg, WPARAM wParam, int x, int y )
{
    vnum = vmode;

    memset( &inpEvt, '\0', sizeof(INPUT_RECORD) );
    inpEvt.EventType = MOUSE_EVENT;

    // translate pixel coordinates into character coordinates
    //
    inpEvt.Event.MouseEvent.dwMousePosition.X = x;
    inpEvt.Event.MouseEvent.dwMousePosition.Y = y;
 
    //debug(F111,"IKTerm::MouseEvent","msg",msg);
    switch( msg )
    {
        case WM_MOUSEMOVE:
        //debug(F111,"IKTerm::MouseEvent","WM_MOUSEMOVE",wParam);
            {
            if( wParam & MK_LBUTTON )
                inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
            else if( wParam & MK_RBUTTON )
                inpEvt.Event.MouseEvent.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
            else if( wParam & MK_MBUTTON )
                inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
            else
                return; // no processing necessary

            inpEvt.Event.MouseEvent.dwEventFlags = MOUSE_MOVED;
            break;
        }

#ifndef NOSCROLLWHEEL
        case WM_MOUSEWHEEL: {
            short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
            char button;
            if( wParam & MK_LBUTTON )
                button = FROM_LEFT_1ST_BUTTON_PRESSED;
            else if( wParam & MK_RBUTTON )
                button = RIGHTMOST_BUTTON_PRESSED;
            else if( wParam & MK_MBUTTON )
                button = FROM_LEFT_2ND_BUTTON_PRESSED;

            inpEvt.Event.MouseEvent.dwEventFlags = MOUSE_WHEELED;
            inpEvt.Event.MouseEvent.dwButtonState = MAKELPARAM(button, zDelta);

            break;
        }
#endif

        case WM_LBUTTONDOWN:
            //debug(F110,"IKTerm::MouseEvent","WM_LBUTTONDOWN",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
            if ( !mouseCaptured )
                SetCapture(hwnd);
            mouseCaptured++;
            break;

        case WM_RBUTTONDOWN:
            //debug(F110,"IKTerm::MouseEvent","WM_RBUTTONDOWN",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
            if ( !mouseCaptured )
                SetCapture(hwnd);
            mouseCaptured++;
            break;

        case WM_MBUTTONDOWN:
            //debug(F110,"IKTerm::MouseEvent","WM_MBUTTONDOWN",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
            if ( !mouseCaptured )
                SetCapture(hwnd);
            mouseCaptured++;
            break;

        case WM_LBUTTONUP:
            //debug(F110,"IKTerm::MouseEvent","WM_LBUTTONUP",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = 0; //FROM_LEFT_1ST_BUTTON_PRESSED;
            mouseCaptured--;
            if ( !mouseCaptured )
                ReleaseCapture();
            break;

        case WM_RBUTTONUP:
            //debug(F110,"IKTerm::MouseEvent","WM_RBUTTONUP",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = 0; //RIGHTMOST_BUTTON_PRESSED;
            mouseCaptured--;
            if ( !mouseCaptured )
                ReleaseCapture();
            break;

        case WM_MBUTTONUP:
            //debug(F110,"IKTerm::MouseEvent","WM_MBUTTONUP",0);
            inpEvt.Event.MouseEvent.dwEventFlags = 0;
            inpEvt.Event.MouseEvent.dwButtonState = 0; //FROM_LEFT_2ND_BUTTON_PRESSED;
            mouseCaptured--;
            if ( !mouseCaptured )
                ReleaseCapture();
            break;

        case WM_LBUTTONDBLCLK:
            //debug(F110,"IKTerm::MouseEvent","WM_LBUTTONDBLCLK",0);
            inpEvt.Event.MouseEvent.dwEventFlags = DOUBLE_CLICK;
            inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_1ST_BUTTON_PRESSED;
            break;

        case WM_RBUTTONDBLCLK:
            //debug(F110,"IKTerm::MouseEvent","WM_RBUTTONDBLCLK",0);
            inpEvt.Event.MouseEvent.dwEventFlags = DOUBLE_CLICK;
            inpEvt.Event.MouseEvent.dwButtonState = RIGHTMOST_BUTTON_PRESSED;
            break;

        case WM_MBUTTONDBLCLK:
            //debug(F110,"IKTerm::MouseEvent","WM_MBUTTONDBLCLK",0);
            inpEvt.Event.MouseEvent.dwEventFlags = DOUBLE_CLICK;
            inpEvt.Event.MouseEvent.dwButtonState = FROM_LEFT_2ND_BUTTON_PRESSED;
            break;

        default:
            debug(F111,"IKTerm::MouseEvent","error",msg);
            return; // error!
    }

    inpEvt.Event.MouseEvent.dwControlKeyState = getControlKeyState();
    //debug(F111,"IKTerm::MouseEvent","dwControlKeyState",inpEvt.Event.MouseEvent.dwControlKeyState);

#ifdef OS2MOUSE
    win32MouseEvent( vnum, inpEvt.Event.MouseEvent );
#endif /* OS2MOUSE */
}
