Artifact ae8e21c84b6448e99c985cff4d927f9dfcf6c56c35e1ed2613a08e184af35dc4:
- Executable file
r38/lisp/csl/cslbase/ipaq.cpp
— part of check-in
[f2fda60abd]
at
2011-09-02 18:13:33
on branch master
— Some historical releases purely for archival purposes
git-svn-id: https://svn.code.sf.net/p/reduce-algebra/code/trunk/historical@1375 2bfe0521-f11c-4a00-b80e-6202646ff360 (user: arthurcnorman@users.sourceforge.net, size: 21524) [annotate] [blame] [check-ins using] [more...]
// // Framework for a Windows CE Application... This is being // built for CE version 420 to run on an IPAQ 4700 (which is a VGA // system). I am not going to guarantee or even worry too much (to start // with at least) about support of other platform variants. // // A C Norman, Codemist Ltd, March 2005 // /* Signature: 048865dc 05-Jan-2006 */ #include <windows.h> #include "res.h" #include <commctrl.h> #include <aygshell.h> #include <stdio.h> HINSTANCE g_hInst; // The current instance HWND g_hwndCB; // The command bar handle HWND g_hWnd = NULL; // main window HFONT hFont = 0; // Private messages that I use within this application. #define WM_REQUESTINPUT (WM_APP+0x01) #define WM_PRINTCHAR (WM_APP+0x02) #define WM_PRINTBUFFER (WM_APP+0x03) #define WM_WORKERQUIT (WM_APP+0x04) HANDLE workerThread; HANDLE mutex1, mutex2; int readRequestPending = 0; char inputLine[200], workerLine[200]; int inputN = 0, workerN = 0, workerP = 0; #define TYPEAHEADSIZE 256 char typeAhead[TYPEAHEADSIZE]; int aheadIn = 0, aheadOut = 0; static RECT windowSize; static SHACTIVATEINFO s_sai; // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); HWND CreateRpCommandBar(HWND); DWORD WINAPI WorkerProc(LPVOID parm); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; // Perform application initialization: if (!InitInstance(hInstance, nCmdShow)) return FALSE; hAccelTable = LoadAccelerators(hInstance, L"ACCEL"); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // It is important to call this function so that the application // will get 'well formed' small icons associated with it. // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, L"ICON"); wc.hCursor = 0; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = L"PPC-CSL"; return RegisterClass(&wc); } #define TEXTSIZE 5000 #define TEXTLINES 33 TCHAR text[TEXTSIZE]; unsigned char textColour[(TEXTSIZE+3)/4]; // 2-bits per character. int textIn = 0; #define TEXT_BLACK 0 #define TEXT_RED 1 #define TEXT_BLUE 2 #define TEXT_GREEN 3 int currentColour = TEXT_BLACK; int textLine[TEXTLINES]; int textX = 0, textY = 0; // Another brief essay about how I do things. In particular the representation // of text. I will allow for LIMITED scrollback (although that is not yet // implemented) by having a circular buffer that actually contains the // text. This is stored in Unicode as a sequence of lines each terminated // with a newline. An array textLine[] identifies that start in this buffer // of the visible lines of the screen: // // text: 0 1 2 3 4 5 6 7 8 9 // - - A B \ C D \ - - ("\" stands for newline here) // ^ ^ // (textOut)---^ ^--- textIn // // textLine[0] 2 // textLine[1] 5 // textLine[2] -1 (indicates no such line) // textLine[3] -1 (and -1 from now on) // // The data in the text buffer wraps round in a circular manner (and thus // when it is displayed it will sometimes take two steps to write a split // line. The textLine array is kept up to date as text is added, and so // values in it are shuffled if a scroll is needed. If adding characters // tries to overwrite the first line then the first stored line is totally // purged. I will assume that even when the maximum number of visible lines // are stored that the text buffer is not full so I do not have to panic // about overflow. // Individual output lines can only be displayed up to character 68, and so I // can truncate after (about) that, which ensures space. This width comes // from using a screen with 480 pixels width and characters that are 7 // pixels wide. // Each character in the text buffer has 2-bits of associated attribute // information which control the colour of display used. // 0: black, 1:red, 2:blue, 3:green // inline int attribute(int n) { return (textColour[n>>2] >> (2*(n & 3))) & 3; } void setAttribute(int n, int v) { int k = n>>2; int b = textColour[k]; int s = 2*(n & 3); int mask = 3 << s; int d = v << s; b = (b & ~mask) | d; textColour[k] = b; } // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { g_hInst = hInstance; // Store instance handle in our global variable //If it is already running, then focus on the window g_hWnd = FindWindow(L"PPC-CSL", L"CSL"); if (g_hWnd) { // set focus to foremost child window // The "| 0x01" is used to bring any owned windows to the // foreground and activate them. SetForegroundWindow((HWND)((ULONG)g_hWnd | 0x01)); return 0; } MyRegisterClass(hInstance); g_hWnd = CreateWindow(L"PPC-CSL", L"CSL", WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (!g_hWnd) return FALSE; // When the main window is created using CW_USEDEFAULT the height of the // menubar (if one is created is not taken into account). So we resize // the window after creating it if a menubar is present GetWindowRect(g_hWnd, &windowSize); if (g_hwndCB) { RECT rcMenuBar; GetWindowRect(g_hwndCB, &rcMenuBar); windowSize.bottom -= (rcMenuBar.bottom - rcMenuBar.top); MoveWindow(g_hWnd, windowSize.left, windowSize.top, windowSize.right - windowSize.left, windowSize.bottom - windowSize.top, FALSE); } LOGFONT f; memset(&f, 0, sizeof(f)); f.lfHeight = 16; f.lfWidth = 8; f.lfQuality = CLEARTYPE_COMPAT_QUALITY; f.lfPitchAndFamily = FIXED_PITCH; hFont = CreateFontIndirect(&f); for (int i=1; i<TEXTLINES; i++) textLine[i] = -1; textLine[0] = 0; text[0] = '\n'; textIn = 0; // text buffer now empty textX = textY = 0; // writing to line 0, at column 0 at present currentColour = TEXT_BLACK; ShowWindow(g_hWnd, nCmdShow); UpdateWindow(g_hWnd); mutex1 = CreateMutex(NULL, 1, NULL); mutex2 = CreateMutex(NULL, 0, NULL); workerThread = CreateThread(NULL, 0, WorkerProc, NULL, 0, NULL); return TRUE; } void deleteChar() { // Do nothing at present... } void insertChar(int ch) { int n = (textIn + 1) % TEXTSIZE; text[textIn] = ch; setAttribute(textIn, currentColour); textIn = n; text[n] = '\n'; // makes the re-painting code simpler } void showChar(int ch) { RECT r; if (ch == '\n') // end a line { insertChar(ch); if (textY == TEXTLINES-1) { for (int i=0; i<TEXTLINES-1; i++) textLine[i] = textLine[i+1]; // If the screen scrolls I will erase the whole background so I can // paint everything again. Maybe if I had a BitBlt to scroll data on the // screen directly I could use that. InvalidateRect(g_hWnd, NULL, TRUE); } else textY++; textLine[textY] = textIn; textX = 0; // now at column 0 return; } if (textX > 70) return; // silently truncate after 70 chars insertChar(ch); r.left = 7*textX; r.right = 7*textX + 7; r.top = 16*textY; r.bottom = 16*textY + 16; // I invalidate without erasing the background here, and when I paint the // characters I do so in non-opaque mode. InvalidateRect(g_hWnd, &r, FALSE); textX++; } char promptString[32] = {'A', ':', ' ', 0}; void displayPrompt() { char *p = promptString; currentColour = TEXT_BLUE; while (*p != 0) showChar(*p++); currentColour = TEXT_RED; promptString[0]++; // make prompts change over time } int colourTable[4] = // These colours are subject to review! { // bbggrr 0x00000000, // black normal program output 0x004000c0, // redish maroon echo of keyboard input 0x00801000, // darkish blue prompt strings 0x00208000 // darkish green (unassigned at first) }; // // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // PURPOSE: Processes messages for the main window. // // WM_COMMAND - process the application menu // WM_PAINT - Paint the main window // WM_DESTROY - post a quit message and return // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; int wmId, wmEvent; PAINTSTRUCT ps; int i, x, y; switch (message) { case WM_CHAR: // I need to write myself an essay about how I will handle input! // // At the start of a run there is no input request pending. The worker // thread can make such a request by posting a message to the GUI thread // and then going into a waiting state until a response arrives. // // If keyboard input arrives when there is no input request pending then // printable characters are inserted into a type-ahead buffer. The only // non-printable character that is handled is BACKSPACE, and that removes // a character from the buffer unless the buffer is empty or the character // that would be removed is an ENTER. At a later stage I will want to // process at least ^\ (exit) and ^C (interrupt) even in this state. // // If an input request arrives I will first put a prompt string on the screen. // Then if the type-ahead buffer is non-empty I will start moving characters // from it into a current-line buffer, echoing each to the screen as I go. If // I find an ENTER I return the line to the worker thread. // // If, even after processing some type-ahead, the current-line is incomplete // I just await further keyboard input. Any that arrives is placed in the // current-line and echoed on the screen. When an ENTER arrives I return to // the worker (and in doing so I clear the input-request-pending status, as I // did on returning a line from typed-ahead characters). // // The proper way to do a lot of this is to handle KEYDOWN and KEYUP // events rather than CHAR. By so doing it becomes possibly to distinguish // between (eg) backspace and ^H. But as best I can see at present I will // need to detect shift and CTRL keys and maintain my own information about // when they are pressed (and perhaps similarly for CAPS-LOCK) and do my own // handling of character repetition. That all feels pretty heavy. So my // strategy will be to start by handling CHAR messages and later (perhaps) // I will detect the other cases. // if (!readRequestPending) { if (wParam == (0x1f & 'H')) { if (aheadIn == aheadOut) return 0; // no type-ahead int p = aheadIn == 0 ? TYPEAHEADSIZE-1 : aheadIn-1; // When the user presses ENTER the line they have just completed becomes // committed. ENTER is represented as Ctl-M if (typeAhead[p] == (0x1f & 'M')) return 0; aheadIn = p; } else { int p = (aheadIn+1) % TYPEAHEADSIZE; if (p == aheadOut) return 0; // no room in the buffer typeAhead[aheadIn] = wParam; aheadIn = p; } return 0; } switch (wParam) { case 0x1f & 'H': // Backspace: if no chars in current line just ignore it. if (inputN == 0) return 0; inputN--; deleteChar(); return 0; default: if (inputN < sizeof(inputLine)-1) { inputLine[inputN++] = wParam; currentColour = TEXT_RED; showChar(wParam); } return 0; case 0x1f & 'M': // ENTER: and there was a read request pending so I must pass data to the // worker thread and release it. inputLine[inputN++] = '\n'; // return as '\n' even if '\r' inputLine[inputN] = 0; // terminate the input line showChar('\n'); currentColour = TEXT_BLACK; readRequestPending = 0; HANDLE m2 = mutex2; ReleaseMutex(mutex1); // permits worker to access the char // The next line has a slight depth. It waits until the worker thread has // accepted the data (and because it was waiting for it that should happen // promptly). But while the worker has control it flips the two variables // mutex1 and mutex2 so that after the WaitForSingleObject here the mutext // actually claimed will be the one referred to be mutex1. WaitForSingleObject(m2, INFINITE); return 0; } // void ce_getline() // { // PostMessage(hWnd, WM_REQUESTINPUT, 0, 0); // WaitForSingleObject(mutex1, INFINITE); // memcpy(workerLine, inputLine, inputN); // workerN = inputN; // HANDLE w = mutex1; // mutext1 = mutex2; // mutex2 = w; // ReleaseMutex(mutex1); // // input data is now in workerLine. // } // The next few messages are ones that my worker thread can post to me. case WM_REQUESTINPUT: inputN = 0; displayPrompt(); while (aheadOut != aheadIn) { int ch = typeAhead[aheadOut]; aheadOut = (aheadOut+1) % TYPEAHEADSIZE; if (ch == (0x1f & 'M')) { inputLine[inputN++] = '\n'; inputLine[inputN] = 0; showChar('\n'); currentColour = TEXT_BLACK; HANDLE m2 = mutex2; ReleaseMutex(mutex1); WaitForSingleObject(m2, INFINITE); return 0; } inputLine[inputN++] = ch; currentColour = TEXT_RED; showChar(ch); } readRequestPending = 1; return 0; case WM_PRINTCHAR: showChar(wParam); return 0; case WM_PRINTBUFFER: // This is pending! { char *s = "<printbuffer>"; while (*s != 0) showChar(*s++); } return 0; case WM_WORKERQUIT: // the worker can send this to terminate the application SendMessage(hWnd, WM_CLOSE, 0, 0); return 0; // Now handlers for some system messages that get treated in fairly // standard manners. case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case IDM_HELP_ABOUT: DialogBox(g_hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); return 0; case IDM_ITEM_QUIT: case IDOK: SendMessage(hWnd, WM_CLOSE, 0, 0); return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } case WM_CREATE: g_hwndCB = CreateRpCommandBar(hWnd); // Initialize the shell activate info structure memset(&s_sai, 0, sizeof(s_sai)); s_sai.cbSize = sizeof(s_sai); return 0; case WM_PAINT: RECT rt; hdc = BeginPaint(hWnd, &ps); if (hFont != 0) SelectObject(hdc, hFont); for (i=0; i<TEXTLINES; i++) { int start = textLine[i], end = textLine[i+1]; if (start == end) continue; // Now the text that I need to draw may have to be drawn in several // segments. There are two reasons for making a break: (a) the line can // be represented by a sequence of chars that wrap around in the circular // text buffer and (b) I may need to deal with colour effects x = 0; y = 16*i; int ch = text[start]; while (ch != '\n' && ch != 0) { int attstart = attribute(start); int next = start; int count = 0; while (ch != '\n' && ch != 0) { next = (next + 1) % TEXTSIZE; count++; if (next == 0) break; // circular buffer wrap int attnext = attribute(next); if (attstart != attnext) break; // colour change ch = text[next]; } SetTextColor(hdc, colourTable[attstart]); ExtTextOut(hdc, x, y, 0, NULL, &text[start], count, NULL); // Note that to cope with cleartype fonts I do not use opaque chars. // Thus I need to clear the background manually at some stage. I think // that two times SHOULD suffice for that: (a) at the start of the // session and (b) clearing a row of stuff when I scroll the screen up. x += 7*count; // I believe that my chars are 7 pixels wide start = next; ch = text[start]; } } EndPaint(hWnd, &ps); return 0; case WM_DESTROY: CommandBar_Destroy(g_hwndCB); if (hFont != 0) DeleteObject(hFont); PostQuitMessage(0); return 0; case WM_ACTIVATE: // Notify shell of our activate message SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE); return 0; case WM_SETTINGCHANGE: SHHandleWMSettingChange(hWnd, wParam, lParam, &s_sai); return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } } HWND CreateRpCommandBar(HWND hwnd) { SHMENUBARINFO mbi; memset(&mbi, 0, sizeof(SHMENUBARINFO)); mbi.cbSize = sizeof(SHMENUBARINFO); mbi.hwndParent = hwnd; mbi.nToolBarId = IDM_MENU; mbi.hInstRes = g_hInst; mbi.nBmpId = 0; mbi.cBmpImages = 0; if (!SHCreateMenuBar(&mbi)) return NULL; return mbi.hwndMB; } // Message handler for the About box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { SHINITDLGINFO shidi; switch (message) { case WM_INITDIALOG: // Create a Done button and size it. shidi.dwMask = SHIDIM_FLAGS; shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN; shidi.hDlg = hDlg; SHInitDialog(&shidi); return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } void ce_getline() { PostMessage(g_hWnd, WM_REQUESTINPUT, 0, 0); WaitForSingleObject(mutex1, INFINITE); memcpy(workerLine, inputLine, inputN); workerN = inputN; workerP = 0; HANDLE w = mutex1; mutex1 = mutex2; mutex2 = w; ReleaseMutex(mutex1); // input data is now in workerLine. } extern "C" { extern int ce_readch(); extern void ce_print(char *s); } int ce_readch() { if (workerN == workerP) ce_getline(); if (workerN == workerP) return EOF; else return workerLine[workerP++]; } void ce_print(char *s) { while (*s != 0) PostMessage(g_hWnd, WM_PRINTCHAR, *s++, 0); } char *argvec[] = { "csl", "-v", "-i", "\\IPAQ File Store\\r38.img", NULL }; extern "C" { extern int fwin_main(int argc, char *argv[]); } DWORD WINAPI WorkerProc(LPVOID parm) { WaitForSingleObject(mutex2, INFINITE); int i; for (i=0; i<7; i++) ce_print("....:....*"); ce_print("\n"); ce_print("This is a message from the worker\n"); // for (i=0; i<2; i++) // { int ch = ce_readch(); // char b[30]; // sprintf(b, "Char %#.2x\n", ch); // ce_print(b); // } // ce_print("Done...\n"); // After finishing I will wait 4 seconds or until an ENTER before // killing the application. // WaitForSingleObject(mutex1, 4000); ce_print("will now try to start fwin_main\n"); fwin_main(4, argvec); ce_print("fwin_main returned: press ENTER to quit\n"); ce_getline(); // wait for user to type ENTER WaitForSingleObject(mutex1, 1000); // then wait 1 second more PostMessage(g_hWnd, WM_WORKERQUIT, 0, 0); return 1; } // end of ipaq.cpp