/* * Squeak IO function implementations for Plan9. * * Author: Alex Franchuk (alex.franchuk@gmail.com) */ #include "sq.h" #include "p9iface.h" #define _PLAN9_SOURCE #define EVT_BUFFER_SIZE 256 #define EVT_THREAD_SLEEP_MS 10 #include #include #include #include #include #include static vlong start_time; static Point mouse_position; static int mouse_button_state = 0; static sqInputEvent evts[EVT_BUFFER_SIZE]; static QLock evt_buf_lock; static int evts_start = 0, evts_end = 0; static int disp_prev_width, disp_prev_height, disp_prev_x, disp_prev_y; static int eventThreadID = -1; static Mousectl *mousectl = NULL; static Keyboardctl *keyboardctl = NULL; /** * @brief Convert unix time to Squeak time * * @param unixTime to to convert * * @return the time according to Squeak! */ time_t convertToSqueakTime(time_t unixTime) { //Account for timezone unixTime += localtime(unixTime)->tzoff; /* Squeak epoch is Jan 1, 1901. Unix epoch is Jan 1, 1970: 17 leap years and 52 non-leap years later than Squeak. */ return unixTime + ((52*365UL + 17*366UL) * 24*60*60UL); } int timeInit(void) { start_time = nsec(); return 0; } int ioInit(void) { mousectl = initmouse(NULL, screen); keyboardctl = initkeyboard(NULL); ctlkeyboard(keyboardctl, "rawon"); return 0; } void ioDestroy(void) { if (eventThreadID != -1) { threadkill(eventThreadID); } closemouse(mousectl); closekeyboard(keyboardctl); } /* Time */ sqInt ioMSecs(void) { vlong now = nsec(); return (now - start_time)/1000000; } sqInt ioMicroMSecs(void) { return ioMSecs(); } sqInt ioSeconds(void) { return (sqInt)convertToSqueakTime(time(NULL)); } /* Miscellaneous */ sqInt ioBeep(void) { printf("beep\n"); return 0; } sqInt ioExit(void) { return ioExitWithErrorCode(0); } sqInt ioExitWithErrorCode(int code) { exit(0); return 0; } static int lastInterruptCheck = 0; sqInt setInterruptCheckCounter(sqInt value); sqInt ioRelinquishProcessorForMicroseconds(sqInt microSeconds) { yield(); int now; now = ioMSecs(); if (now - lastInterruptCheck > (1000/25)) { /* avoid thrashing intr checks from 1ms loop in idle proc */ setInterruptCheckCounter(-1000); /* ensure timely poll for semaphore activity */ lastInterruptCheck = now; } return 0; } sqInt ioDisablePowerManager(sqInt disableIfNonZero) { return true; } /* Display */ sqInt ioForceDisplayUpdate(void) { flushimage(display,1); return true; } sqInt ioFormPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth, double hScale, double vScale, sqInt landscapeFlag) { return 0; } sqInt ioSetFullScreen(sqInt fullScreen) { if (fullScreen) { ushort w = screen->r.max.x - screen->r.min.x; ushort h = screen->r.max.y - screen->r.min.y; disp_prev_x = screen->r.min.x; disp_prev_y = screen->r.min.y; disp_prev_width = w; disp_prev_height = h; } else { positionWindow(disp_prev_x, disp_prev_y); } return ioSetDisplayMode(disp_prev_width, disp_prev_height, screen->depth, fullScreen); } sqInt ioScreenSize(void) { ushort w = screen->r.max.x - screen->r.min.x; ushort h = screen->r.max.y - screen->r.min.y; return (w<<16)|h; } sqInt ioScreenDepth(void) { return 32; } sqInt ioSetCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY) { uchar* cursorBits = (uchar*)pointerForOop(cursorBitsIndex); int i; Cursor c; c.offset.x = offsetX; c.offset.y = offsetY; for (i = 0; i < 16; i++) { c.clr[i*2] = 0; c.clr[i*2+1] = 0; c.set[i*2] = cursorBits[i*4+3]; c.set[i*2+1] = cursorBits[i*4+2]; } setcursor(mousectl, &c); return 0; } sqInt ioSetCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY) { uchar* cursorBits = (uchar*)pointerForOop(cursorBitsIndex); uchar* maskBits = (uchar*)pointerForOop(cursorMaskIndex); int i; Cursor c; c.offset.x = offsetX; c.offset.y = offsetY; for (i = 0; i < 16; i++) { c.clr[i*2] = (~cursorBits[i*4+3] & maskBits[i*4+3]); c.clr[i*2+1] = (~cursorBits[i*4+2] & maskBits[i*4+2]); c.set[i*2] = cursorBits[i*4+3]; c.set[i*2+1] = cursorBits[i*4+2]; } setcursor(mousectl, &c); return 0; } sqInt ioSetCursorARGB(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY) { //Not supported in Plan9 return 0; } static void sqToP9Bits(uchar* sqBits, uchar* p9Bits, int width, int height, int left, int right, int top, int bottom) { int region_w = right-left; for (int y = top; y < bottom; y++) for (int x = left; x < right; x++) { p9Bits[((y-top)*region_w+(x-left))*3] = sqBits[(y*width+x)*4]; p9Bits[((y-top)*region_w+(x-left))*3+1] = sqBits[(y*width+x)*4+1]; p9Bits[((y-top)*region_w+(x-left))*3+2] = sqBits[(y*width+x)*4+2]; } } sqInt ioShowDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth, sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB) { Rectangle r; r.min.x = affectedL; r.max.x = affectedR; r.min.y = affectedT; r.max.y = affectedB; ulong chan; uchar* dispBits = (uchar*)pointerForOop(dispBitsIndex); size_t region_w = affectedR-affectedL, region_h = affectedB-affectedT; uchar* buf = (uchar*)malloc(region_w*region_h*3); if (buf == NULL) return 0; sqToP9Bits(dispBits, buf, width, height, affectedL, affectedR, affectedT, affectedB); switch (depth) { case 1: chan = GREY1; break; case 2: chan = GREY2; break; case 4: chan = CMAP8; break; case 8: chan = CMAP8; break; case 16: chan = RGB16; break; case 32: chan = RGB24; break; default: return -1; } Image* s = allocimage(display, r, chan, 0, DNofill); if (s == 0) { return -1; } Image* mask = allocimage(display, r, chan, 0, DOpaque); if (mask == 0) { freeimage(s); return -1; } loadimage(s, r, buf, region_w*region_h*3); Point e; e.x = 0; e.y = 0; draw(screen, screen->r, s, mask, e); freeimage(mask); freeimage(s); free(buf); return 0; } sqInt ioHasDisplayDepth(sqInt depth) { if (depth == 32) return 1; else return 0; } sqInt ioSetDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag) { if (fullscreenFlag) { Rectangle ssize = display->image->r; positionWindow(ssize.min.x,ssize.min.y); width = ssize.max.x - ssize.min.x - 1; height = ssize.max.y - ssize.min.y - 1; } resizeWindow(width,height); return 0; } /* Mouse/Keyboard */ sqInt ioGetButtonState(void) { int left = mouse_button_state & 1; int middle = (mouse_button_state & 2) >> 1; int right = (mouse_button_state & 4) >> 2; return RedButtonBit*left | YellowButtonBit*middle | BlueButtonBit*right; } sqInt ioMousePoint(void) { return ((mouse_position.x & 0xFFFF) << 16) | (mouse_position.y & 0xFFFF); } sqInt ioGetKeystroke(void) { int k = evts_start; while (k != evts_end) { if (evts[k].type == EventTypeKeyboard) { evts_start = (k+1) % EVT_BUFFER_SIZE; return ((sqKeyboardEvent)evts[k]).charCode; } k = (k+1) % EVT_BUFFER_SIZE; } return 0; } sqInt ioPeekKeystroke(void) { int k = evts_start; while (k != evts_end) { if (evts[k].type == EventTypeKeyboard) { return ((sqKeyboardEvent)evts[k]).charCode; } k = (k+1) % EVT_BUFFER_SIZE; } return 0; } /* Return 1 if values were set, or 0 if no charcode could be made */ static int splitRune(int rune, int* charCode, int* modifiers) { static int escapeNext = 0; *modifiers = 0; if (escapeNext) { *modifiers |= CommandKeyBit; escapeNext = 0; } //Escape key, next character should be Command-(key) else if (rune == 0x1) { escapeNext = 1; return 0; } *charCode = rune; return 1; } static int p9toSqueakKey(int charcode) { switch (charcode) { case '\n': return '\r'; case 61453: return 1; //Home case 61454: return 30; //Up case 61455: return 11; //Page up case 61457: return 28; //Left case 61458: return 29; //Right case 61459: return 12; //Page down case 61460: return 5; //Insert case 61464: return 4; //End case 63488: return 31; //Down default: return charcode; } } /* Retrieve pending events, and return the number of events */ static int getPendingEvents(void) { int count = 0; while (1) { Mouse mouse; Rune rune; int charCode, modifiers; int mouse_event = nbrecv(mousectl->c, (void*)&mouse); int resize_event = nbrecv(mousectl->resizec, NULL); int kbd_event = nbrecv(keyboardctl->c, (void*)&rune); if (mouse_event) { //Process mouse event mouse_position.x = mouse.xy.x - screen->r.min.x; mouse_position.y = mouse.xy.y - screen->r.min.y; mouse_button_state = mouse.buttons; qlock(&evt_buf_lock); if ((evts_end+2)%EVT_BUFFER_SIZE != evts_start) { sqMouseEvent* evt = (sqMouseEvent*)&evts[evts_end++]; evts_end %= EVT_BUFFER_SIZE; evt->type = EventTypeMouse; evt->timeStamp = ioMSecs(); evt->x = mouse_position.x; evt->y = mouse_position.y; evt->buttons = ioGetButtonState(); evt->modifiers = 0; evt->reserved1 = evt->windowIndex = 0; count++; } qunlock(&evt_buf_lock); } if (resize_event) { getwindow(display, Refnone); fullDisplayUpdate(); } if (kbd_event && splitRune(rune, &charCode, &modifiers)) { //Process keyboard event qlock(&evt_buf_lock); if ((evts_end+2)%EVT_BUFFER_SIZE != evts_start) { sqKeyboardEvent* evt = (sqKeyboardEvent*)&evts[evts_end++]; evts_end %= EVT_BUFFER_SIZE; evt->type = EventTypeKeyboard; evt->timeStamp = ioMSecs(); evt->pressCode = EventKeyChar; evt->charCode = p9toSqueakKey(charCode); evt->modifiers = modifiers; evt->reserved1 = evt->windowIndex = 0; count++; } qunlock(&evt_buf_lock); } if (!(resize_event || kbd_event || mouse_event)) break; } return count; } void eventThread(void* data) { int evt_semaphore = (int)data; while (1) { int count = getPendingEvents(); while (count--) { signalSemaphoreWithIndex(evt_semaphore); } sleep(EVT_THREAD_SLEEP_MS); } } sqInt ioProcessEvents(void) { if (eventThreadID == -1) getPendingEvents(); return 0; } sqInt ioSetInputSemaphore(sqInt semaIndex) { if (semaIndex == 0) { success(0); } else { //Initialize thread and mouse/keyboard eventThreadID = proccreate(eventThread,(void*)semaIndex,2048); } return true; } sqInt ioGetNextEvent(sqInputEvent *evt) { qlock(&evt_buf_lock); if (evts_start == evts_end) { qunlock(&evt_buf_lock); ioProcessEvents(); qlock(&evt_buf_lock); } if (evts_start != evts_end) { memcpy(evt, &evts[evts_start++], sizeof(sqInputEvent)); evts_start %= EVT_BUFFER_SIZE; qunlock(&evt_buf_lock); return true; } qunlock(&evt_buf_lock); return false; }