home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tv20cpp.zip / src / system.cpp < prev    next >
C/C++ Source or Header  |  1999-06-07  |  15KB  |  670 lines

  1. #define INCL_SUB
  2. #define INCL_MOU
  3. #define INCL_DOSPROCESS
  4. #include <os2.h>
  5.  
  6. #define Uses_TButton
  7. #define Uses_TColorSelector
  8. #define Uses_TDeskTop
  9. #define Uses_TDirListBox
  10. #define Uses_TDrawBuffer
  11. #define Uses_TEvent
  12. #define Uses_TEventQueue
  13. #define Uses_TFrame
  14. #define Uses_THistory
  15. #define Uses_TIndicator
  16. #define Uses_TKeys
  17. #define Uses_TListViewer
  18. #define Uses_TMenuBox
  19. #define Uses_TOutlineViewer
  20. #define Uses_TScreen
  21. #define Uses_TScrollBar
  22. #define Uses_TStatusLine
  23. #include <tvision/tv.h>
  24.  
  25. #include <conio.h>
  26. #include <fstream.h>
  27. #include <stdlib.h>
  28. #include <ctype.h>
  29. #include <string.h>
  30. #include <dir.h>
  31. #include <stdio.h>
  32. #include <sys/stat.h>
  33.  
  34. /*
  35.  * This is the delay in ms before the first evMouseAuto is generated when the
  36.  * user holds down a mouse button.
  37.  */
  38. #define DELAY_AUTOCLICK_FIRST    400
  39.  
  40. /*
  41.  * This is the delay in ms between next evMouseAuto events.  Must be greater
  42.  * than DELAY_SIGALRM (see below).
  43.  */
  44. #define DELAY_AUTOCLICK_NEXT    100
  45.  
  46. /*
  47.  * This is the time limit in ms within button presses are recognized as
  48.  * double-click events.  Used only under FreeBSD because Gpm has its own
  49.  * double-click detecting machanism.
  50.  */
  51. #define DELAY_DOUBLECLICK    300
  52.  
  53. /*
  54.  * This is the time limit in ms within Esc-key sequences are detected as
  55.  * Alt-letter sequences.  Useful when we can't generate Alt-letter sequences
  56.  * directly.
  57.  */
  58. #define DELAY_ESCAPE        400
  59.  
  60. /*
  61.  * This is the delay in ms between consecutive SIGALRM signals.  This
  62.  * signal is used to generate evMouseAuto and cmSysWakeup events.
  63.  */
  64. #define DELAY_SIGALRM        100
  65.  
  66. /*
  67.  * This broadcast event is used to update the StatusLine.
  68.  */
  69. #define DELAY_WAKEUP        200
  70.  
  71. /* this array stores the corresponding ncurses attribute for each TV color */
  72.  
  73. static unsigned attributeMap[256];
  74.  
  75. ushort TEventQueue::doubleDelay = 8;
  76. Boolean TEventQueue::mouseReverse = False;
  77.  
  78. ushort TScreen::screenMode;
  79. uchar TScreen::screenWidth;
  80. uchar TScreen::screenHeight;
  81. ushort *TScreen::screenBuffer;
  82.  
  83. static TEvent *evIn;        /* message queue system */
  84. static TEvent *evOut;
  85. static TEvent evQueue[eventQSize];
  86. static TPoint msWhere;        /* mouse coordinates */
  87. static char env[PATH_MAX];    /* value of the TVOPT environment variable */
  88. static int curX, curY;        /* current cursor coordinates */
  89. static int currentTime;        /* current timer value */
  90. static int doRepaint;        /* should redraw the screen ? */
  91. static int doResize;        /* resize screen ? */
  92. static int evLength;        /* number of events in the queue */
  93. static int msOldButtons;    /* mouse button status */
  94. static ofstream xlog;        /* a logging file */
  95. static int msRepaint = 0;
  96.  
  97. /* key modifiers */
  98.  
  99. #define MALT        (kbLeftAlt | kbRightAlt)
  100. #define MCTRL        (kbLeftCtrl | kbRightCtrl)
  101. #define MSHIFT        (kbLeftShift | kbRightShift)
  102.  
  103. /* key types */
  104.  
  105. #define TALT        0x01        /* alt-letter key */
  106.  
  107. HKBD KbdHandle;
  108. KBDKEYINFO pkbci;
  109. USHORT rc;
  110. KBDINFO StatData;
  111.  
  112. typedef struct
  113. {
  114.     int    code;
  115.     int    scancode;
  116.     int    shiftstate;
  117.     int    modifiers;
  118.     short    out;
  119. }
  120. keym_t;
  121.  
  122. static keym_t keym[] =
  123. {
  124. #include "src/keycodes"
  125. };
  126.  
  127. static keym_t keymext[] =
  128. {
  129. #include "src/keycodes.ext"
  130. };
  131.  
  132. // Mouse options
  133.  
  134. HMOU msHandle;
  135. MOUEVENTINFO msEventInfo;
  136. USHORT ReadType = 0;
  137. static void mouseHandle();
  138. static void msPutEvent(TEvent &event, int buttons, int flags, int what);
  139.  
  140. /*
  141.  * A simple class which implements a timer.
  142.  */
  143.  
  144. class Timer
  145. {
  146.     int limit;
  147. public:
  148.     Timer() { limit = -1; }
  149.     int isExpired() { return limit != -1 && currentTime >= limit; }
  150.     int isRunning() { return limit != -1; }
  151.     void start(int timeout) { limit = currentTime + timeout; }
  152.     void stop() { limit = -1; }
  153. };
  154.  
  155. //static Timer kbEscTimer;    /* time limit to detect Esc-key sequences */
  156. static Timer msAutoTimer;    /* time when generate next cmMouseAuto */
  157. static Timer wakeupTimer;    /* time when generate next cmWakeup */
  158. static Timer msDoubleTimer;    /* time limit to detect double-click events */
  159. static int msFlag;        /* set if there are mouse events */
  160. static int msUseArrow;        /* use arrow pointer */
  161.  
  162. // Codepage definitions
  163. HAB hab;
  164. ULONG Codepage=866;
  165. ULONG Country=7;
  166.  
  167. /*
  168.  * GENERAL FUNCTIONS
  169.  */
  170.  
  171. #define LOG(s) xlog << s << endl
  172.  
  173. inline int range(int test, int min, int max)
  174. {
  175.     return test < min ? min : test > max ? max : test;
  176. }
  177. /*
  178.  * KEYBOARD FUNCTIONS
  179.  */
  180.  
  181. /*
  182.  * Builds a keycode from code and modifiers.
  183.  */
  184.  
  185. int    EscapeState=0;
  186.  
  187. static int kbMapKey(int code, int scancode, int *shiftstate)
  188. {
  189.     keym_t *best = NULL, *p;
  190.  
  191.     if( EscapeState == 0 ){
  192.     for (p = keym; p < keym + sizeof(keym) / sizeof(keym_t); p++)
  193.     {
  194.         if (p->code == code && p->scancode == scancode && p->shiftstate == *shiftstate )
  195.         {
  196.             best = p;
  197.             *shiftstate = p->modifiers;
  198.             break;
  199.         }
  200.     }
  201.     }
  202.     else
  203.     {
  204.     for (p = keymext; p < keymext + sizeof(keym) / sizeof(keym_t); p++)
  205.     {
  206.         if (p->code == code && p->scancode == scancode && p->shiftstate == *shiftstate )
  207.         {
  208.             best = p;
  209.             *shiftstate = p->modifiers;
  210.             break;
  211.         }
  212.     }
  213.     }
  214.     if (best != NULL) return best->out;    /* keycode found */
  215.     if ( 0 < code <= 255) return code;    /* it is an ascii character */
  216.     return kbNoKey;        /* unknown code */
  217. }
  218.  
  219. /*
  220.  * Reads a key from the keyboard.
  221.  */
  222. static void kbHandle()
  223. {
  224.     int code, modifiers;
  225.     int kbReady;
  226.  
  227. NextChar:
  228.     kbReady = 0;
  229.     rc = KbdCharIn( &pkbci, 1, KbdHandle );
  230.     if( rc ) LOG("KbdCharIn error, rc = " << rc);
  231.     
  232.     kbReady |= ( (pkbci.fbStatus & 64) > 0 ) ? 1 : 0;
  233.     if( kbReady == 0 ) return;
  234.     
  235.     modifiers = 0;
  236.     modifiers |= ( (pkbci.fsState & 3) > 0 ) ? kbShift : 0;    // Shift state
  237.     modifiers |= ( (pkbci.fsState & 4) > 0 ) ? kbCtrlShift : 0;    // Ctrl state
  238.     modifiers |= ( (pkbci.fsState & 8) > 0 ) ? kbAltShift : 0;    // Alt state
  239.     
  240.     if( EscapeState == 0 )
  241.     {
  242.         if( pkbci.chChar == 91 && pkbci.chScan == 26 && modifiers == 0 )
  243.         { EscapeState = 1; goto NextChar; }
  244.     }
  245.     
  246.     if ((code = kbMapKey( pkbci.chChar, pkbci.chScan, &modifiers)) != kbNoKey)
  247.     {
  248.         TEvent event;
  249.  
  250.         event.what = evKeyDown;
  251.         event.keyDown.keyCode = code;
  252.         event.keyDown.controlKeyState = modifiers;
  253.         TScreen::putEvent(event);
  254.         msRepaint = 1;
  255.     }
  256.     EscapeState = 0;
  257. }
  258.  
  259. /*
  260.  * CLASS FUNCTIONS
  261.  */
  262.  
  263. /*
  264.  * TScreen constructor.
  265.  */
  266.  
  267. TScreen::TScreen()
  268. {
  269.     char *p = getenv("TVLOG");
  270.     if (p != NULL && *p != '\0')
  271.     {
  272.         xlog.open(p);
  273.         LOG("using environment variable TVLOG=" << p);
  274.     }
  275.     else xlog.open("/dev/null");
  276.     env[0] = '\0';
  277.     if ((p = getenv("TVOPT")) != NULL)
  278.     {
  279.         LOG("using environment variable TVOPT=" << p);
  280.         for (char *d = env; *p != '\0'; p++) *d++ = tolower(*p);
  281.     }
  282.  
  283.     /* acquire screen size */
  284.  
  285.     screenWidth = 80;
  286.     screenHeight = 25;
  287.     screenHeight--;
  288.     LOG("screen size is " << (int) screenWidth << "x" <<
  289.         (int) screenHeight);
  290.     screenBuffer = new ushort[screenWidth * screenHeight];
  291.     drawCursor( 0 );
  292.  
  293.     /* internal stuff */
  294.  
  295.     curX = curY = 0;
  296.     currentTime = doRepaint = doResize = evLength = 0;
  297.     evIn = evOut = &evQueue[0];
  298.     msOldButtons = msWhere.x = msWhere.y = 0;
  299.  
  300.     /* catch useful signals */
  301.  
  302. /*
  303.     struct sigaction dfl_handler;
  304.  
  305.     dfl_handler.sa_handler = sigHandler;
  306.     sigemptyset(&dfl_handler.sa_mask);
  307.     dfl_handler.sa_flags = SA_RESTART;
  308.  
  309.     sigaction(SIGALRM, &dfl_handler, NULL);
  310.     sigaction(SIGCONT, &dfl_handler, NULL);
  311.     sigaction(SIGINT, &dfl_handler, NULL);
  312.     sigaction(SIGQUIT, &dfl_handler, NULL);
  313.     sigaction(SIGTSTP, &dfl_handler, NULL);
  314.     sigaction(SIGWINCH, &dfl_handler, NULL);
  315. */
  316.  
  317. rc = KbdOpen( &KbdHandle );if( rc ) LOG("KbdOpen error");
  318. rc = KbdGetFocus( 0, KbdHandle );if( rc ) LOG("KbdGetFocus error, rc = " << rc);
  319. StatData.cb = sizeof( StatData );StatData.fsMask = 0x4;
  320. StatData.chTurnAround = 0;StatData.fsInterim = 0;
  321. StatData.fsState = 0;
  322. rc = KbdSetStatus( &StatData, KbdHandle );if( rc ) LOG("KbdSetStatus error, rc = " << rc);
  323.  
  324. // Now Mouse!
  325. rc = MouOpen( NULL, &msHandle );
  326. if( rc )
  327. {
  328.         LOG("MouOpen error, rc = %d" << rc);
  329. }
  330. SCALEFACT sf;
  331. sf.rowScale = 12;
  332. sf.colScale = 6;
  333. rc = MouSetScaleFact( &sf, msHandle );
  334. if( rc )
  335. {
  336.         LOG("MouSetScaleFact error, rc = %d" << rc);
  337. }
  338.  
  339. msAutoTimer.stop();
  340. msDoubleTimer.stop();
  341. msFlag = msOldButtons = 0;
  342.  
  343. hab = WinInitialize( 0 );
  344. if( hab == NULLHANDLE )
  345.     LOG( "Translation of national chars is not supported" );
  346. }
  347.  
  348. /*
  349.  * TScreen destructor.
  350.  */
  351.  
  352. TScreen::~TScreen()
  353. {
  354. rc = KbdFreeFocus( KbdHandle );
  355. if( rc ) LOG("KbdFreeFocus error, rc = " << rc);
  356. KbdClose( KbdHandle );
  357. delete[] screenBuffer;
  358. LOG("terminated");
  359. if( hab != NULLHANDLE ) WinTerminate( hab );
  360. }
  361.  
  362. void TScreen::resume()
  363. {
  364.     KbdGetFocus( 0, KbdHandle );
  365.     doRepaint++;
  366. }
  367.  
  368. void TScreen::suspend()
  369. {
  370. rc = KbdFreeFocus( KbdHandle );
  371. }
  372.  
  373. /*
  374.  * Gets an event from the queue.
  375.  */
  376.  
  377. void TScreen::getEvent(TEvent &event)
  378. {
  379.     event.what = evNothing;
  380.     if (doRepaint > 0)
  381.     {
  382.         doRepaint = 0;
  383.         event.message.command = cmSysRepaint;
  384.         event.what = evCommand;
  385.     }
  386.     else if (doResize > 0)
  387.     {
  388.         clrscr();    /* blank the screen */
  389.         doResize = 0;
  390.     }
  391.     else if (evLength > 0)    /* handles pending events */
  392.     {
  393.         evLength--;
  394.         event = *evOut;
  395.         if (++evOut >= &evQueue[eventQSize]) evOut = &evQueue[0];
  396.     }
  397.     else
  398.     {
  399.         mouseHandle();
  400.         kbHandle();
  401.         DosSleep( 5 );
  402.     }
  403. }
  404.  
  405. /*
  406.  * Generates a beep.
  407.  */
  408.  
  409. void TScreen::makeBeep()
  410. {
  411.     putch( 7 );
  412. }
  413.  
  414. /*
  415.  * Puts an event in the queue.  If the queue is full the event will be
  416.  * discarded.
  417.  */
  418.  
  419. void TScreen::putEvent(TEvent &event)
  420. {
  421.     if (evLength < eventQSize)
  422.     {
  423.         evLength++;
  424.         *evIn = event;
  425.         if (++evIn >= &evQueue[eventQSize]) evIn = &evQueue[0];
  426.     }
  427. }
  428.  
  429. /*
  430.  * Hides or shows the cursor.
  431.  */
  432.  
  433. void TScreen::drawCursor(int show)
  434. {
  435.     if (show) _setcursortype( _NORMALCURSOR );    /* cursor normal */
  436.     else _setcursortype( _NOCURSOR );    /* cursor invisible */
  437. }
  438.  
  439. /*
  440.  * Hides or shows the mouse pointer.
  441.  */
  442.  
  443. void TScreen::drawMouse(int show)
  444. {
  445. if( show )
  446. rc = MouDrawPtr( msHandle );
  447. else
  448. {
  449.     NOPTRRECT PtrArea;
  450.     PtrArea.row = 0;
  451.     PtrArea.col = 0;
  452.     PtrArea.cRow = screenHeight;
  453.     PtrArea.cCol = screenWidth;
  454.     rc = MouRemovePtr( &PtrArea, msHandle );
  455. }
  456. }
  457.  
  458. /*
  459.  * Moves the cursor to another place.
  460.  */
  461.  
  462. void TScreen::moveCursor(int x, int y)
  463. {
  464. //    gotoxy( x+1, y+1 );
  465.     VioSetCurPos( y, x, 0 );
  466.     curX = x;
  467.     curY = y;
  468. }
  469.  
  470. /*
  471.  * Draws a line of text on the screen.
  472.  */
  473.  
  474. void TScreen::writeRow(int dst, ushort *src, int len)
  475. {
  476. char buff[4096]; int i;
  477.  
  478. int x = dst % TScreen::screenWidth;
  479. int y = dst / TScreen::screenWidth;
  480.  
  481. for( i = 0 ; i < len ; i++ )
  482.     {
  483.     buff[i*2] = (char)(src[i] & 0xff);
  484.     buff[(i*2)+1] = (char)((src[i] & 0xff00) >> 8);
  485.     }
  486. VioWrtCellStr( buff, len*2, y, x, 0);
  487.  
  488. }
  489.  
  490. /*
  491.  * Returns the length of a file.
  492.  */
  493.  
  494. long int filelength(int fd)
  495. {
  496.     struct stat s;
  497.     fstat(fd, &s);
  498.     return s.st_size;
  499. }
  500.  
  501. /*
  502.  * Expands a path into its directory and file components.
  503.  */
  504.  
  505. void expandPath(const char *path, char *dir, char *file)
  506. {
  507.     char *tag = strrchr( (char *)path, '/');
  508.     char *drivetag = NULL;
  509.     /* the path is in the form /dir1/dir2/file ? */
  510.  
  511.     if (tag != NULL)
  512.     {
  513.         strcpy(file, tag + 1);
  514.         strncpy(dir, path, tag - path + 1);
  515.         dir[tag - path + 1] = '\0';
  516.     }
  517.     else
  518.     {
  519.         /* there is only the file name */
  520.         drivetag = strrchr( (char*)path, ':' );
  521.         if( drivetag == NULL )
  522.             {
  523.             strcpy(file, path);
  524.             dir[0] = '\0';
  525.             }
  526.         else
  527.             {
  528.             strcpy(file, ++drivetag);
  529.             strcpy(dir, path);
  530.             dir[drivetag - path] = '/';
  531.             dir[drivetag - path +1] = 0;
  532.             }
  533.     }
  534. }
  535.  
  536. void fexpand(char *path)
  537. {
  538.     char dir[PATH_MAX];
  539.     char file[PATH_MAX];
  540.     char oldPath[PATH_MAX];
  541.  
  542.     expandPath(path, dir, file);
  543. //    LOG( "After expandPath: path = " << path << " dir = " << dir << " file = " << file );
  544.     getcwd(oldPath, sizeof(oldPath));
  545.     if( strlen(dir) > 3 )
  546.     if( dir[strlen(dir) -1 ] == '/' ) dir[strlen(dir) -1 ] = 0;
  547.     Slash2backSlash( dir );
  548.     chdir(dir);
  549. //    LOG( "chdir: dir = " << dir );
  550.     getcwd(dir, sizeof(dir));
  551. //    LOG( "getcwd: dir = " << dir );
  552.     backSlash2Slash( dir );
  553.     if( strlen(dir) > 0 )
  554.     if( dir[strlen(dir) -1 ] == '/' ) dir[strlen(dir) -1 ] = 0;
  555.     chdir(oldPath);
  556.     if (strcmp(dir, "/") == 0) sprintf(path, "/%s", file);
  557.     else sprintf(path, "%s/%s", dir, file);
  558. //    LOG( "After fexpand: path = " << path << " dir = " << dir << " file = " << file );
  559. }
  560.  
  561. void backSlash2Slash( char * path )
  562. {
  563. while( *path )
  564.     {
  565.     if( *path == '\\' ) *path = '/';
  566.     ++path;
  567.     }
  568.  
  569. }
  570.  
  571. void Slash2backSlash( char * path )
  572. {
  573. while( *path )
  574.     {
  575.     if( *path == '/' ) *path = '\\';
  576.     ++path;
  577.     }
  578.  
  579. }
  580.  
  581. /*
  582.  * Handles mouse events.
  583.  *
  584.  * This function was changed to fit the message handling of the ncurses mouse
  585.  * support that differs from the gpm by the following:
  586.  *
  587.  * - sends mouse clicks, double clicks, etc;
  588.  * - sends the message only once: we needn't lock messages;
  589.  * - doesn't send mouse drag and mouse move messages (or may be I couldn't
  590.  *   find the way to get them).
  591.  */
  592.  
  593. static void mouseHandle()
  594. {
  595.  
  596.     TEvent event;
  597.  
  598.     rc = MouReadEventQue( &msEventInfo, &ReadType, msHandle );
  599.     if( rc )
  600.         LOG("MouReadEventQue error, rc = " << rc);
  601.     if( msEventInfo.time == 0 )
  602.         {
  603.         if( msRepaint == 0 ) return;
  604.         msRepaint = 0;
  605.         PTRLOC loc;
  606.         loc.col = msWhere.x;
  607.         loc.row = msWhere.y;
  608.         MouSetPtrPos( &loc, msHandle );
  609.         return;
  610.         }
  611.         
  612.     //    event.mouse.controlKeyState = kbReadShiftState();
  613.     event.mouse.controlKeyState = 0;
  614.     event.mouse.where.x = msEventInfo.col;
  615.     event.mouse.where.y = msEventInfo.row;
  616.     /* convert button bits to TV standard */
  617.  
  618.     int buttons = 0;
  619.     if ( msEventInfo.fs & 0x06 ) buttons |= mbLeftButton;
  620.     if ( msEventInfo.fs & 0x18 ) buttons |= mbRightButton;
  621.  
  622.     /* is mouse moved ? */
  623.  
  624. //    LOG( "Button release state:" << buttons << "msOldButtons: " << msOldButtons );
  625.     if ( event.mouse.where != msWhere )
  626.     {
  627.         msAutoTimer.stop();
  628.         msDoubleTimer.stop();
  629.         msPutEvent(event, buttons, meMouseMoved, evMouseMove);
  630.         msWhere = event.mouse.where;
  631.     }
  632.     if ( buttons > msOldButtons )    /* is any button pressed ? */
  633.     {
  634.         msAutoTimer.start(DELAY_AUTOCLICK_FIRST);
  635.         if (msDoubleTimer.isRunning() && !msDoubleTimer.isExpired())
  636.         {
  637.             msDoubleTimer.stop();
  638.             msPutEvent(event, buttons & ~msOldButtons, meDoubleClick, evMouseDown);
  639.         }
  640.         else
  641.         {
  642.             msDoubleTimer.start(DELAY_DOUBLECLICK);
  643.             msPutEvent(event, buttons & ~msOldButtons, 0, evMouseDown);
  644.         }
  645.     }
  646.     if ( buttons < msOldButtons )    /* is any button released ? */
  647.     {
  648.         msAutoTimer.stop();
  649.         msPutEvent(event, buttons, 0, evMouseUp);
  650.     }
  651.     msOldButtons = buttons;
  652. }
  653.  
  654. static void msPutEvent(TEvent &event, int buttons, int flags, int what)
  655. {
  656.     msRepaint = 1;
  657.     event.mouse.buttons = 0;
  658.     event.mouse.eventFlags = flags;
  659.     event.what = what;
  660.     if (TEventQueue::mouseReverse)    /* swap buttons ? */
  661.     {
  662.         if (buttons & mbLeftButton) event.mouse.buttons |=
  663.             mbRightButton;
  664.         if (buttons & mbRightButton) event.mouse.buttons |=
  665.             mbLeftButton;
  666.     }
  667.     else event.mouse.buttons = buttons;    /* no swapping */
  668.     TScreen::putEvent(event);
  669. }
  670.