#include "windows.h" /* required for all Windows applications */ #include "cppext.h" #include "longque.h" #include "button.h" #include "slider.h" #include "winmain.h" /* specific to this program */ #include "stdlib.h" #include "string.h" #include "stdio.h" #include "textio.h" #include "mmsystem.h" #include #include // for Sleep() #include "stdio.h" #include "resource.h" #include "typein.h" #include "xlispfns.h" #include "winfun.h" //#define THUMBTRACK // tries to get continuous scrollbars with SB_THUMBTRACK messages. // doesn't work -- zone 1 doesn't seem to wake up until the button-up. //#include "saudio.h" //#define D if(0) HWND buttons[NUMBUTTONS]; HWND sliders[NUMSLIDERS]; HWND textinput; HWND textoutput; HFONT hfont; HWND alt_win; WNDPROC alt_proc; extern "C" { HINSTANCE hInst; /* current instance */ HWND hMainWindow; /* main window handle */ } /* HINSTANCE hInst; /* current instance */ /* HWND hMainWindow; /* main window handle */ int quit_value; // return value from WinMain int abort_flag = 0; // there's some state here: // when we get a message from the input queue, it is an entire // string. We save the string and a pointer to the next char // char *get_ascii_string = NULL; char *next_ascii = NULL; //asciiwait -- wait for ascii input char wait_ascii() { if (next_ascii && *next_ascii) { char c = *next_ascii++; if (c == '\r') c = '\n'; _RPT1(_CRT_WARN, "|%c|", c); return c; } if (get_ascii_string) { _RPT2(_CRT_WARN, "free get_ascii_string %x %s\n", get_ascii_string, get_ascii_string); free(get_ascii_string); get_ascii_string = NULL; next_ascii = NULL; } // no input, so look for Windows messages while (typein::queue.emptyp() && !abort_flag) { process_win_events(USE_GET); } if (abort_flag == ABORT_LEVEL) return ABORT_CHAR; if (abort_flag == BREAK_LEVEL) return BREAK_CHAR; get_ascii_string = (char *) typein::queue.remove(); _RPT2(_CRT_WARN, "removed %x: %s\n", get_ascii_string, get_ascii_string); edit_append(get_ascii_string); next_ascii = get_ascii_string; return wait_ascii(); } //process_win_events -- receive and handle windows by either: // USE_PEEK: non-blocking // USE_GET: blocks void process_win_events(int method) { MSG msg; edit_append(""); // flush the output if (method == USE_GET) { //blocks until at least a message arrives if (GetMessage(&msg, //msg stored here NULL, //receive ALL application messages NULL,NULL)) //no msg filtering { //standard windows loop TranslateMessage(&msg); //posts another msg if there is a virtual to WM_CHAR mapping DispatchMessage(&msg); //calls object's receive function } else { //is this ever entered??? quit_value = msg.wParam; abort_flag = ABORT_CHAR; exit(0); } } else { //default: process all messges that already exist (non-blocking) while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE | PM_NOYIELD)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } //STUFF TO PROVIDE CONSOLE------------------------------------------------- //terminate strings and strip out LF int lf2crlf(char *dest, char *src) { char *d = dest; while (*src) { if (*src == '\n') { *dest++ = '\r'; } *dest++ = *src++; } *dest = EOS; return dest - d; /* string length */ } #define EDIT_HIGH_WATER 10000 #define EDIT_LOW_WATER 9000 #define EABUFFMAX 110 static char eabuff[EABUFFMAX]; /* edit_append buffer */ static char eabuffx = 0; static void edit_append2(char *txt2); /* edit_append -- optimizes output by buffering * * call with empty string to flush buffer */ void edit_append(char *txt) { /* new algorithm to deal with long strings on input: * if input is longer than 50, insert a zero and * call recursively; then undo the zero and continue. */ char txt2[100]; while (strlen(txt) > 50) { char temp = txt[50]; txt[50] = 0; edit_append(txt); /* strlen(txt) == 50 */ txt = txt + 50; txt[0] = temp; } int len = lf2crlf(txt2, txt); if ((eabuffx + len + 1 > EABUFFMAX) || ((len == 0) && eabuffx)) { edit_append2(eabuff); eabuffx = 0; } strcpy(eabuff + eabuffx, txt2); eabuffx += len; } static void edit_append2(char *txt2) { int len; int lines; if (*txt2 == '\b') { // special case: erase last character long len = SendMessage(textoutput, WM_GETTEXTLENGTH, (WPARAM) 0, (LPARAM) 0); if (len > 0) { // select the last character: SendMessage(textoutput, EM_SETSEL, (WPARAM) len - 1, (LPARAM) -1); // delete the last character: SendMessage(textoutput, EM_REPLACESEL, (WPARAM) 0, (LPARAM) ((LPSTR) "")); } return; } // to put insertion point at the end, first select // everything ... //wparam is UINT ; lparam is LONG SendMessage(textoutput, EM_SETSEL, (WPARAM) 0, (LPARAM) -1); // then remove selection, leaving cursor at the end: SendMessage(textoutput, EM_SETSEL, (WPARAM) -1, (LPARAM) -1); // now, replacement actually appends to the buffer: SendMessage(textoutput, EM_REPLACESEL, (WPARAM) 0, (LPARAM) ((LPSTR) txt2)); // if the number of characters exceeds EDIT_HIGH_WATER, then // trim the number of characters to EDIT_LOW_WATER by deleting // all lines up to the one containing total-EDIT_LOW_WATER lines = (int) SendMessage(textoutput, EM_GETLINECOUNT, (WPARAM) 0, (LPARAM) 0); len = (int) SendMessage(textoutput, EM_LINEINDEX, (WPARAM)(lines - 1), (LPARAM) 0); len += (int) SendMessage(textoutput, EM_LINELENGTH, (WPARAM)(lines - 1), (LPARAM) 0); if (len > EDIT_HIGH_WATER) { //these SendMessages operate to completion lines = (int) SendMessage(textoutput, EM_LINEFROMCHAR, (WPARAM)(len - EDIT_LOW_WATER), (LPARAM) 0); len = (int) SendMessage(textoutput, EM_LINEINDEX, (WPARAM)(lines), (LPARAM) 0); SendMessage(textoutput, EM_SETSEL, (WPARAM) 0, (LPARAM)(len)); SendMessage(textoutput, EM_REPLACESEL, (WPARAM) 0, (LPARAM) ((LPSTR) "")); } } //THE STUFF REQUIRED BY WINDOWS APPLICATIONS---------------------------------- // we will subclass the textinput window with a window procedure that detects // the Enter key (hex 0x0d) and sets a flag. Then in the main window proc, // when an EN_CHANGE message is received, we will know the user typed Enter, // so we can transfer the text to XLISP. // // This would be a good place also to add editing characters to scroll through // previous entries. // WNDPROC DefaultEditWndProc = NULL; #define ENTER_KEY 0x0d bool enter_flag = false; long CALLBACK EditWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_CHAR) { if (wParam == ENTER_KEY) enter_flag = true; _RPT1(_CRT_WARN, "wm_char is %x\n", wParam); } return CallWindowProc(DefaultEditWndProc, hWnd, message, wParam, lParam); } //All applications need to register BOOL InitApplication(HINSTANCE hInstance /* current instance */) { WNDCLASS wc; /* Fill in window class structure with parameters that describe the */ /* main window. */ wc.style = NULL; /* Class style(s). */ wc.lpfnWndProc = MainWndProc; /* Function to retrieve messages for */ /* windows of this class. */ wc.cbClsExtra = 0; /* No per-class extra data. */ wc.cbWndExtra = 0; /* No per-window extra data. */ wc.hInstance = hInstance; /* Application that owns the class. */ wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NYCON)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MAINMENU"; /* Name of menu resource in .RC file. */ wc.lpszClassName = "CMTWClass"; /* Name used in call to CreateWindow. */ /* Register the window class and return success/failure code. */ return (RegisterClass(&wc)); } static char *button_names[NUMBUTTONS] = { "Load File", "Reload File", "Replay Sound", "Break", "Top", "Up", "Info", "F1", "F2", "F3", "F4" }; //each time this application is run, the following program must be run BOOL InitInstance( HINSTANCE hInstance, /* Current instance identifier. */ int nCmdShow) /* Param for first ShowWindow() call. */ { int i; RECT rect; LRESULT rslt; /* Save the instance handle in static variable, which will be used in */ /* many subsequent calls from this application to Windows. */ hInst = hInstance; /* Create a main window for this application instance. */ hMainWindow = CreateWindow( "CMTWClass", /* See RegisterClass() call. */ "Nyquist", /* Text for window title bar. */ WS_OVERLAPPEDWINDOW, /* Window style. */ CW_USEDEFAULT, /* Default horizontal position. */ CW_USEDEFAULT, /* Default vertical position. */ CW_USEDEFAULT, /* Default width. */ CW_USEDEFAULT, /* Default height. */ NULL, /* Overlapped windows have no parent. */ NULL, /* Use the window class menu. */ hInstance, /* This instance owns this window. */ NULL /* Pointer not needed. */ ); /* If window could not be created, return "failure" */ if (!hMainWindow) return (FALSE); GetClientRect(hMainWindow, (LPRECT) &rect); /* Make the window visible; update its client area; and return "success" */ const int button_y = 25; // size in pixels const int slider_x = 20; const int slider_gap = 1; const int input_height = 48; // how high is input type-in edit control int slider_xsum = slider_x * NUMSLIDERS; int button_ysum = button_y * NUMBUTTONS_VERT; ShowWindow(hMainWindow, nCmdShow); /* Show the window */ UpdateWindow(hMainWindow); /* Sends WM_PAINT message */ for (i = 0; i < NUMBUTTONS; i++) { int x = 0; int y = i * button_y; int width = slider_xsum; if (i > 2) { y = (3 + (i - 3) / 2) * button_y; width = width / 2; if ((i & 1) == 0) x = width; } buttons[i] = CreateWindow("Button", //lpszClassName button_names[i],//windowName BS_PUSHBUTTON | //Style: 1) PB WS_CHILD | // 2) must reside w/in parent WS_VISIBLE, // 3) initially visible x, y, width, button_y, hMainWindow, //owner window (HMENU)(IDC_BUTTON+i), //&buttonsH[i], //child window Id hInstance, //application instance NULL); //WM_CREATE argument //activate current window & display w/current size and position ShowWindow(buttons[i], SW_SHOW); //update (nonempty) client area via WM_PAINT UpdateWindow(buttons[i]); } for (i=0; i= IDC_BUTTON && wid < IDC_BUTTON + NUMBUTTONS) { SetFocus(textinput); /* get focus back */ type_this(button_msgs[wid - IDC_BUTTON]); } else if (wid == IDC_EDIT_INPUT) { if (code == EN_CHANGE) { if (enter_flag) { enter_flag = false; long len = SendMessage(hwndCtrl, WM_GETTEXTLENGTH, 0, 0); len++; // allow for terminating null character char *buffer = (char *) malloc(len); SendMessage(hwndCtrl, WM_GETTEXT, (WPARAM) len, (LPARAM) buffer); _RPT2(_CRT_WARN, "inserting %x: %s\n", buffer, buffer); typein::handler(buffer); SendMessage(hwndCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) ""); } } } else if (wid == IDC_EDIT) { if (code == EN_CHANGE) { } else { } } else { /*Let Windows process it*/ printf("unhandled: message %d\n", message); return (DefWindowProc(hWnd, message, wParam, lParam)); } break; } case WM_DESTROY: //message: window being destroyed PostQuitMessage(0); break; #ifdef MOUSEBUTTON_INPUT case WM_LBUTTONDOWN: { //message: left mouse button pressed while cursor in client area //never used, need SetCaputure??? int /*???*/ xPos LOWORD(lParam); int /*???*/ yPos HIWORD(lParam); //int /*???*/ fwKeys; virtual keys not used!!! buffer[0] = '<'; _itoa(xPos,buffer+1,10/*radix*/); i = strlen(buffer); buffer[i++] = ','; _itoa(yPos,buffer+i,10/*radix*/); i = strlen(buffer); buffer[i++] = '>'; buffer[i] = 0; edit_append(buffer); break; #endif #ifdef DEBUGINPUT //WM_USER msg codes are from the callbacks------------------------- case WM_USER_TIMEOUT: { //dummy message just unblocks GetMessage break; } //remaining wMsg codes generated elsewhere------------------------------------ case WM_KILLFOCUS: { //message: this window's loosing focus, focus must be set to new window HWND newFocus = (HWND) wParam; if (newFocus == textoutput) { //why not also set buttons, sliders...??? SetFocus(hWnd); } break; } case WM_VSCROLL: { //message: control bar scroll I/O //where is window scroll bar handled??? //why is this not also a WM_COMMAND??? HWND hwndCtrl = (HWND) lParam; short int nPos = HIWORD(wParam); int code = LOWORD(wParam); int i; for (i = 0; i < NUMSLIDERS; i++) { if (sliders[i] == hwndCtrl) { int pos = GetScrollPos(hwndCtrl /*parent*/, SB_CTL /*control*/); switch (code) { case SB_LINEUP: pos--; break; case SB_LINEDOWN: pos++; break; case SB_PAGEUP: pos -= 10; break; case SB_PAGEDOWN: pos += 10; break; case SB_ENDSCROLL: break; //why not continue??? case SB_THUMBTRACK: #ifdef THUMBTRACK pos = nPos; break; #else continue; // no silly multiple messages #endif //break; case SB_THUMBPOSITION: #ifndef THUMBTRACK pos = nPos; #endif break; case SB_TOP: pos = 0; break; case SB_BOTTOM: pos = 127; break; default: continue; //avoid SetScrollPos } // SetScrollRange() set the range to 0-127, but clip just to make sure: if (pos < 0) pos = 0; if (pos > 127) pos = 127; if (code != SB_ENDSCROLL #ifdef THUMBTRACK && code != SB_THUMBTRACK #endif ) { //ctrlevents.insert(CTRLEVENT(IDC_SLIDER + i, 127 - pos)); } SetScrollPos(hwndCtrl /*parent*/, SB_CTL /*control sb*/, pos /*new position*/, TRUE /*redraw*/); break; } } break; } #endif default: //Pass on all unproccessed messages return (DefWindowProc(hWnd, message, wParam, lParam)); } return (NULL); //all messages currently return this... } /**************************************************************************** FUNCTION: About(HWND, unsigned, WORD, LONG) PURPOSE: Processes messages for "About" dialog box MESSAGES: WM_INITDIALOG - initialize dialog box WM_COMMAND - Input received COMMENTS: No initialization is needed for this particular dialog box, but TRUE must be returned to Windows. Wait for user to click on "Ok" button, then close the dialog box. ****************************************************************************/ extern "C" BOOL CALLBACK About( HWND hDlg, /* window handle of the dialog box */ unsigned message, /* type of message */ WORD wParam, /* message-specific information */ LONG lParam) { switch (message) { case WM_INITDIALOG: /* message: initialize dialog box */ return (TRUE); case WM_COMMAND: /* message: received a command */ if (wParam == IDOK /* "OK" box selected? */ || wParam == IDCANCEL) /* System menu close command? */ { EndDialog(hDlg, TRUE); /* Exits the dialog box */ return TRUE; } break; } return FALSE; /* Didn't process a message */ } /* Load File support */ extern "C" void RegisterWindow(HWND w, WNDPROC p) { alt_win = w; alt_proc = p; }