diff options
Diffstat (limited to 'erts/emulator/drivers/win32/win_con.c')
-rw-r--r-- | erts/emulator/drivers/win32/win_con.c | 2259 |
1 files changed, 2259 insertions, 0 deletions
diff --git a/erts/emulator/drivers/win32/win_con.c b/erts/emulator/drivers/win32/win_con.c new file mode 100644 index 0000000000..2202ca655f --- /dev/null +++ b/erts/emulator/drivers/win32/win_con.c @@ -0,0 +1,2259 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#define UNICODE 1 +#define _UNICODE 1 +#include <tchar.h> +#include <stdio.h> +#include "sys.h" +#include <windowsx.h> +#include "resource.h" +#include "erl_version.h" +#include <commdlg.h> +#include <commctrl.h> +#include "erl_driver.h" +#include "win_con.h" + +#define ALLOC(X) malloc(X) +#define REALLOC(X,Y) realloc(X,Y) +#define FREE(X) free(X) + +#ifndef STATE_SYSTEM_INVISIBLE +/* Mingw problem with oleacc.h and WIN32_LEAN_AND_MEAN */ +#define STATE_SYSTEM_INVISIBLE 0x00008000 +#endif + +#define WM_CONTEXT (0x0401) +#define WM_CONBEEP (0x0402) +#define WM_SAVE_PREFS (0x0403) + +#define USER_KEY TEXT("Software\\Ericsson\\Erlang\\") TEXT(ERLANG_VERSION) + +#define FRAME_HEIGHT ((2*GetSystemMetrics(SM_CYEDGE))+(2*GetSystemMetrics(SM_CYFRAME))+GetSystemMetrics(SM_CYCAPTION)) +#define FRAME_WIDTH (2*GetSystemMetrics(SM_CXFRAME)+(2*GetSystemMetrics(SM_CXFRAME))+GetSystemMetrics(SM_CXVSCROLL)) + +#define LINE_LENGTH canvasColumns +#define COL(_l) ((_l) % LINE_LENGTH) +#define LINE(_l) ((_l) / LINE_LENGTH) + +#ifdef UNICODE +/* + * We use a character in the invalid unicode range + */ +#define SET_CURSOR (0xD8FF) +#else +/* + * XXX There is no escape to send a character 0x80. Fortunately, + * the ttsl driver currently replaces 0x80 with an octal sequence. + */ +#define SET_CURSOR (0x80) +#endif + +#define SCAN_CODE_BREAK 0x46 /* scan code for Ctrl-Break */ + + +typedef struct ScreenLine_s { + struct ScreenLine_s* next; + struct ScreenLine_s* prev; + int width; +#ifdef HARDDEBUG + int allocated; +#endif + int newline; /* Ends with hard newline: 1, wrapped at end: 0 */ + TCHAR *text; +} ScreenLine_t; + +extern Uint32 *lbuf; /* The current line buffer */ +extern int llen; /* The current line length */ +extern int lpos; + +HANDLE console_input_event; +HANDLE console_thread = NULL; + +#define DEF_CANVAS_COLUMNS 80 +#define DEF_CANVAS_ROWS 26 + +#define BUFSIZE 4096 +#define MAXBUFSIZE 32768 +typedef struct { + TCHAR *data; + int size; + int wrPos; + int rdPos; +} buffer_t; + +static buffer_t inbuf; +static buffer_t outbuf; + +static CHOOSEFONT cf; + +static TCHAR szFrameClass[] = TEXT("FrameClass"); +static TCHAR szClientClass[] = TEXT("ClientClass"); +static HWND hFrameWnd; +static HWND hClientWnd; +static HWND hTBWnd; +static HWND hComboWnd; +static HANDLE console_input; +static HANDLE console_output; +static int cxChar,cyChar, cxCharMax; +static int cxClient,cyClient; +static int cyToolBar; +static int iVscrollPos,iHscrollPos; +static int iVscrollMax,iHscrollMax; +static int nBufLines; +static int cur_x; +static int cur_y; +static int canvasColumns = DEF_CANVAS_COLUMNS; +static int canvasRows = DEF_CANVAS_ROWS; +static ScreenLine_t *buffer_top,*buffer_bottom; +static ScreenLine_t* cur_line; +static POINT editBeg,editEnd; +static BOOL fSelecting = FALSE; +static BOOL fTextSelected = FALSE; +static HKEY key; +static BOOL has_key = FALSE; +static LOGFONT logfont; +static DWORD fgColor; +static DWORD bkgColor; +static FILE *logfile = NULL; +static RECT winPos; +static BOOL toolbarVisible; +static BOOL destroyed = FALSE; + +static int lines_to_save = 1000; /* Maximum number of screen lines to save. */ + +#define TITLE_BUF_SZ 256 + +struct title_buf { + TCHAR *name; + TCHAR buf[TITLE_BUF_SZ]; +}; + +static TCHAR *erlang_window_title = TEXT("Erlang"); + +static unsigned __stdcall ConThreadInit(LPVOID param); +static LRESULT CALLBACK ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); +static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam); +static BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); +static ScreenLine_t *ConNewLine(void); +static void DeleteTopLine(void); +static void ensure_line_below(void); +static ScreenLine_t *GetLineFromY(int y); +static void LoadUserPreferences(void); +static void SaveUserPreferences(void); +static void set_scroll_info(HWND hwnd); +static void ConCarriageFeed(int); +static void ConScrollScreen(void); +static BOOL ConChooseFont(HWND hwnd); +static void ConFontInitialize(HWND hwnd); +static void ConSetFont(HWND hwnd); +static void ConChooseColor(HWND hwnd); +static void DrawSelection(HWND hwnd, POINT pt1, POINT pt2); +static void InvertSelectionArea(HWND hwnd); +static void OnEditCopy(HWND hwnd); +static void OnEditPaste(HWND hwnd); +static void OnEditSelAll(HWND hwnd); +static void GetFileName(HWND hwnd, TCHAR *pFile); +static void OpenLogFile(HWND hwnd); +static void CloseLogFile(HWND hwnd); +static void LogFileWrite(TCHAR *buf, int n); +static int write_inbuf(TCHAR *data, int n); +static void init_buffers(void); +static void AddToCmdHistory(void); +static int write_outbuf(TCHAR *data, int num_chars); +static void ConDrawText(HWND hwnd); +static BOOL (WINAPI *ctrl_handler)(DWORD); +static HWND InitToolBar(HWND hwndParent); +static void window_title(struct title_buf *); +static void free_window_title(struct title_buf *); +static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags); + +#define CON_VPRINTF_BUF_INC_SIZE 1024 + +static erts_dsprintf_buf_t * +grow_con_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need) +{ + char *buf; + size_t size; + + ASSERT(dsbufp); + + if (!dsbufp->str) { + size = (((need + CON_VPRINTF_BUF_INC_SIZE - 1) + / CON_VPRINTF_BUF_INC_SIZE) + * CON_VPRINTF_BUF_INC_SIZE); + buf = (char *) ALLOC(size * sizeof(char)); + } + else { + size_t free_size = dsbufp->size - dsbufp->str_len; + + if (need <= free_size) + return dsbufp; + + size = need - free_size + CON_VPRINTF_BUF_INC_SIZE; + size = (((size + CON_VPRINTF_BUF_INC_SIZE - 1) + / CON_VPRINTF_BUF_INC_SIZE) + * CON_VPRINTF_BUF_INC_SIZE); + size += dsbufp->size; + buf = (char *) REALLOC((void *) dsbufp->str, + size * sizeof(char)); + } + if (!buf) + return NULL; + if (buf != dsbufp->str) + dsbufp->str = buf; + dsbufp->size = size; + return dsbufp; +} + +static int con_vprintf(char *format, va_list arg_list) +{ + int res,i; + erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_con_vprintf_buf); + res = erts_vdsprintf(&dsbuf, format, arg_list); + if (res >= 0) { + TCHAR *tmp = ALLOC(dsbuf.str_len*sizeof(TCHAR)); + for (i=0;i<dsbuf.str_len;++i) { + tmp[i] = dsbuf.str[i]; + } + write_outbuf(tmp, dsbuf.str_len); + FREE(tmp); + } + if (dsbuf.str) + FREE((void *) dsbuf.str); + return res; +} + +void +ConInit(void) +{ + unsigned tid; + + console_input = CreateSemaphore(NULL, 0, 1, NULL); + console_output = CreateSemaphore(NULL, 0, 1, NULL); + console_input_event = CreateManualEvent(FALSE); + console_thread = (HANDLE *) _beginthreadex(NULL, 0, + ConThreadInit, + 0, 0, &tid); + + /* Make all erts_*printf on stdout and stderr use con_vprintf */ + erts_printf_stdout_func = con_vprintf; + erts_printf_stderr_func = con_vprintf; +} + +/* + ConNormalExit() is called from erl_exit() when the emulator + is stopping. If the exit has not been initiated by this + console thread (WM_DESTROY or ID_BREAK), the function must + invoke the console thread to save the user preferences. +*/ +void +ConNormalExit(void) +{ + if (!destroyed) + SendMessage(hFrameWnd, WM_SAVE_PREFS, 0L, 0L); +} + +void +ConWaitForExit(void) +{ + ConPrintf("\n\nAbnormal termination\n"); + WaitForSingleObject(console_thread, INFINITE); +} + +void ConSetCtrlHandler(BOOL (WINAPI *handler)(DWORD)) +{ + ctrl_handler = handler; +} + +int ConPutChar(Uint32 c) +{ + TCHAR sbuf[1]; +#ifdef HARDDEBUG + fprintf(stderr,"ConPutChar: %d\n",(int) c); + fflush(stderr); +#endif + sbuf[0] = c; + write_outbuf(sbuf, 1); + return 1; +} + +static int GetXFromLine(HDC hdc, int hscroll, int xpos,ScreenLine_t *pLine) +{ + SIZE size; + int hscrollPix = hscroll * cxChar; + + if (pLine == NULL) { + return 0; + } + + if (pLine->width < xpos) { + return (canvasColumns-hscroll)*cxChar; + } + /* Not needed (?): SelectObject(hdc,CreateFontIndirect(&logfont)); */ + if (GetTextExtentPoint32(hdc,pLine->text,xpos,&size)) { +#ifdef HARDDEBUG + fprintf(stderr,"size.cx:%d\n",(int)size.cx); + fflush(stderr); +#endif + if (hscrollPix >= size.cx) { + return 0; + } + return ((int) size.cx) - hscrollPix; + } else { + return (xpos-hscroll)*cxChar; + } +} + +static int GetXFromCurrentY(HDC hdc, int hscroll, int xpos) { + return GetXFromLine(hdc, hscroll, xpos, GetLineFromY(cur_y)); +} + +void ConSetCursor(int from, int to) +{ TCHAR cmd[9]; + int *p; + //DebugBreak(); + cmd[0] = SET_CURSOR; + /* + * XXX Expect trouble on CPUs which don't allow misaligned read and writes. + */ + p = (int *)&cmd[1]; + *p++ = from; + *p = to; + write_outbuf(cmd, 1 + (2*sizeof(int)/sizeof(TCHAR))); +} + +void ConPrintf(char *format, ...) +{ + va_list va; + + va_start(va, format); + (void) con_vprintf(format, va); + va_end(va); +} + +void ConBeep(void) +{ + SendMessage(hClientWnd, WM_CONBEEP, 0L, 0L); +} + +int ConReadInput(Uint32 *data, int num_chars) +{ + TCHAR *buf; + int nread; + WaitForSingleObject(console_input,INFINITE); + nread = num_chars = min(num_chars,inbuf.wrPos-inbuf.rdPos); + buf = &inbuf.data[inbuf.rdPos]; + inbuf.rdPos += nread; + while (nread--) + *data++ = *buf++; + if (inbuf.rdPos >= inbuf.wrPos) { + inbuf.rdPos = 0; + inbuf.wrPos = 0; + ResetEvent(console_input_event); + } + ReleaseSemaphore(console_input,1,NULL); + return num_chars; +} + +int ConGetKey(void) +{ + Uint32 c; + WaitForSingleObject(console_input,INFINITE); + ResetEvent(console_input_event); + inbuf.rdPos = inbuf.wrPos = 0; + ReleaseSemaphore(console_input,1,NULL); + WaitForSingleObject(console_input_event,INFINITE); + ConReadInput(&c, 1); + return (int) c; +} + +int ConGetColumns(void) +{ + return (int) canvasColumns; /* 32bit atomic on windows */ +} + +int ConGetRows(void) { + return (int) canvasRows; +} + + +static HINSTANCE hInstance; +extern HMODULE beam_module; + +static unsigned __stdcall +ConThreadInit(LPVOID param) +{ + MSG msg; + WNDCLASSEX wndclass; + int iCmdShow; + STARTUPINFO StartupInfo; + HACCEL hAccel; + int x, y, w, h; + struct title_buf title; + + /*DebugBreak();*/ + hInstance = GetModuleHandle(NULL); + StartupInfo.dwFlags = 0; + GetStartupInfo(&StartupInfo); + iCmdShow = StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? + StartupInfo.wShowWindow : SW_SHOWDEFAULT; + + LoadUserPreferences(); + + /* frame window class */ + wndclass.cbSize = sizeof (wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT; + wndclass.lpfnWndProc = FrameWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1)); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = szFrameClass; + wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1)); + RegisterClassExW (&wndclass); + + /* client window class */ + wndclass.cbSize = sizeof (wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndclass.lpfnWndProc = ClientWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(1)); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = CreateSolidBrush(bkgColor); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = szClientClass; + wndclass.hIconSm = LoadIcon (hInstance, MAKEINTRESOURCE(1)); + RegisterClassExW (&wndclass); + + InitCommonControls(); + init_buffers(); + + nBufLines = 0; + buffer_top = cur_line = ConNewLine(); + cur_line->next = buffer_bottom = ConNewLine(); + buffer_bottom->prev = cur_line; + + /* Create Frame Window */ + window_title(&title); + hFrameWnd = CreateWindowEx(0, szFrameClass, title.name, + WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, + CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, + NULL,LoadMenu(beam_module,MAKEINTRESOURCE(1)), + hInstance,NULL); + free_window_title(&title); + + /* XXX OTP-5522: + The window position is not saved correctly and if the window + is closed when minimized, it's not possible to start werl again + with the window open. Temporary fix so far is to ignore saved values + and always start with initial settings. */ + /* Original: if (winPos.left == -1) { */ + /* Temporary: if (1) { */ + if (1) { + + /* initial window position */ + x = 0; + y = 0; + w = cxChar*LINE_LENGTH+FRAME_WIDTH+GetSystemMetrics(SM_CXVSCROLL); + h = cyChar*30+FRAME_HEIGHT; + } else { + /* saved window position */ + x = winPos.left; + y = winPos.top; + w = winPos.right - x; + h = winPos.bottom - y; + } + SetWindowPos(hFrameWnd, NULL, x, y, w, h, SWP_NOZORDER); + + ShowWindow(hFrameWnd, iCmdShow); + UpdateWindow(hFrameWnd); + + hAccel = LoadAccelerators(beam_module,MAKEINTRESOURCE(1)); + + ReleaseSemaphore(console_input, 1, NULL); + ReleaseSemaphore(console_output, 1, NULL); + + + /* Main message loop */ + while (GetMessage (&msg, NULL, 0, 0)) + { + if (!TranslateAccelerator(hFrameWnd,hAccel,&msg)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + /* + PostQuitMessage() results in WM_QUIT which makes GetMessage() + return 0 (which stops the main loop). Before we return from + the console thread, the ctrl_handler is called to do erl_exit. + */ + (*ctrl_handler)(CTRL_CLOSE_EVENT); + return msg.wParam; +} + +static LRESULT CALLBACK +FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + int cy,i,bufsize; + TCHAR c; + unsigned long l; + TCHAR buf[128]; + struct title_buf title; + + switch (iMsg) { + case WM_CREATE: + /* client window creation */ + window_title(&title); + hClientWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClientClass, title.name, + WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hwnd, (HMENU)0, hInstance, NULL); + free_window_title(&title); + hTBWnd = InitToolBar(hwnd); + UpdateWindow (hClientWnd); + return 0; + case WM_SIZE : + if (IsWindowVisible(hTBWnd)) { + SendMessage(hTBWnd,TB_AUTOSIZE,0,0L); + GetWindowRect(hTBWnd,&r); + cy = r.bottom-r.top; + } else cy = 0; + MoveWindow(hClientWnd,0,cy,LOWORD(lParam),HIWORD(lParam)-cy,TRUE); + return 0; + case WM_ERASEBKGND: + return 1; + case WM_SETFOCUS : + CreateCaret(hClientWnd, NULL, cxChar, cyChar); + SetCaretPos(GetXFromCurrentY(GetDC(hwnd),0,cur_x), (cur_y-iVscrollPos)*cyChar); + ShowCaret(hClientWnd); + return 0; + case WM_KILLFOCUS: + HideCaret(hClientWnd); + DestroyCaret(); + return 0; + case WM_INITMENUPOPUP : + if (lParam == 0) /* File popup menu */ + { + EnableMenuItem((HMENU)wParam, IDMENU_STARTLOG, + logfile ? MF_GRAYED : MF_ENABLED); + EnableMenuItem((HMENU)wParam, IDMENU_STOPLOG, + logfile ? MF_ENABLED : MF_GRAYED); + return 0; + } + else if (lParam == 1) /* Edit popup menu */ + { + EnableMenuItem((HMENU)wParam, IDMENU_COPY, + fTextSelected ? MF_ENABLED : MF_GRAYED); + EnableMenuItem((HMENU)wParam, IDMENU_PASTE, + IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); + return 0; + } + else if (lParam == 3) /* View popup menu */ + { + CheckMenuItem((HMENU)wParam,IDMENU_TOOLBAR, + IsWindowVisible(hTBWnd) ? MF_CHECKED : MF_UNCHECKED); + return 0; + } + break; + case WM_NOTIFY: + switch (((LPNMHDR) lParam)->code) { + case TTN_NEEDTEXT: + { + LPTOOLTIPTEXT lpttt; + lpttt = (LPTOOLTIPTEXT) lParam; + lpttt->hinst = hInstance; + /* check for combobox handle */ + if (lpttt->uFlags&TTF_IDISHWND) { + if ((lpttt->hdr.idFrom == (UINT) hComboWnd)) { + lstrcpy(lpttt->lpszText,TEXT("Command History")); + break; + } + } + /* check for toolbar buttons */ + switch (lpttt->hdr.idFrom) { + case IDMENU_COPY: + lstrcpy(lpttt->lpszText,TEXT("Copy (Ctrl+C)")); + break; + case IDMENU_PASTE: + lstrcpy(lpttt->lpszText,TEXT("Paste (Ctrl+V)")); + break; + case IDMENU_FONT: + lstrcpy(lpttt->lpszText,TEXT("Fonts")); + break; + case IDMENU_ABOUT: + lstrcpy(lpttt->lpszText,TEXT("Help")); + break; + } + } + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDMENU_STARTLOG: + OpenLogFile(hwnd); + return 0; + case IDMENU_STOPLOG: + CloseLogFile(hwnd); + return 0; + case IDMENU_EXIT: + SendMessage(hwnd, WM_CLOSE, 0, 0L); + return 0; + case IDMENU_COPY: + if (fTextSelected) + OnEditCopy(hClientWnd); + return 0; + case IDMENU_PASTE: + OnEditPaste(hClientWnd); + return 0; + case IDMENU_SELALL: + OnEditSelAll(hClientWnd); + return 0; + case IDMENU_FONT: + if (ConChooseFont(hClientWnd)) { + ConSetFont(hClientWnd); + } + SaveUserPreferences(); + return 0; + case IDMENU_SELECTBKG: + ConChooseColor(hClientWnd); + SaveUserPreferences(); + return 0; + case IDMENU_TOOLBAR: + if (toolbarVisible) { + ShowWindow(hTBWnd,SW_HIDE); + toolbarVisible = FALSE; + } else { + ShowWindow(hTBWnd,SW_SHOW); + toolbarVisible = TRUE; + } + GetClientRect(hwnd,&r); + PostMessage(hwnd,WM_SIZE,0,MAKELPARAM(r.right,r.bottom)); + return 0; + case IDMENU_ABOUT: + DialogBox(beam_module,TEXT("AboutBox"),hwnd,AboutDlgProc); + return 0; + case ID_COMBOBOX: + switch (HIWORD(wParam)) { + case CBN_SELENDOK: + i = SendMessage(hComboWnd,CB_GETCURSEL,0,0); + if (i != CB_ERR) { + buf[0] = 0x01; /* CTRL+A */ + buf[1] = 0x0B; /* CTRL+K */ + bufsize = SendMessage(hComboWnd,CB_GETLBTEXT,i,(LPARAM)&buf[2]); + if (bufsize != CB_ERR) + write_inbuf(buf,bufsize+2); + SetFocus(hwnd); + } + break; + case CBN_SELENDCANCEL: + break; + } + break; + case ID_BREAK: /* CTRL+BRK */ + /* pass on break char if the ctrl_handler is disabled */ + if ((*ctrl_handler)(CTRL_C_EVENT) == FALSE) { + c = 0x03; + write_inbuf(&c,1); + } + return 0; + } + break; + case WM_KEYDOWN : + switch (wParam) { + case VK_UP: c = 'P'-'@'; break; + case VK_DOWN : c = 'N'-'@'; break; + case VK_RIGHT : c = 'F'-'@'; break; + case VK_LEFT : c = 'B'-'@'; break; + case VK_DELETE : c = 'D' -'@'; break; + case VK_HOME : c = 'A'-'@'; break; + case VK_END : c = 'E'-'@'; break; + case VK_RETURN : AddToCmdHistory(); return 0; + case VK_PRIOR : /* PageUp */ + PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEUP, 0); + return 0; + case VK_NEXT : /* PageDown */ + PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEDOWN, 0); + return 0; + default: return 0; + } + write_inbuf(&c, 1); + return 0; + case WM_CHAR: + c = (TCHAR)wParam; + write_inbuf(&c,1); + return 0; + case WM_CLOSE : + break; + case WM_DESTROY : + SaveUserPreferences(); + destroyed = TRUE; + PostQuitMessage(0); + return 0; + case WM_SAVE_PREFS : + SaveUserPreferences(); + return 0; + } + return DefWindowProc(hwnd, iMsg, wParam, lParam); +} + +static BOOL +Client_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) +{ + ConFontInitialize(hwnd); + cur_x = cur_y = 0; + iVscrollPos = 0; + iHscrollPos = 0; + return TRUE; +} + +static void +Client_OnPaint(HWND hwnd) +{ + ScreenLine_t *pLine; + int x,y,i,iTop,iBot; + PAINTSTRUCT ps; + RECT rcInvalid; + HDC hdc; + + hdc = BeginPaint(hwnd, &ps); + rcInvalid = ps.rcPaint; + hdc = ps.hdc; + iTop = max(0, iVscrollPos + rcInvalid.top/cyChar); + iBot = min(nBufLines, iVscrollPos + rcInvalid.bottom/cyChar+1); + pLine = GetLineFromY(iTop); + for (i = iTop; i < iBot && pLine != NULL; i++) { + y = cyChar*(i-iVscrollPos); + x = -cxChar*iHscrollPos; + TextOut(hdc, x, y, &pLine->text[0], pLine->width); + pLine = pLine->next; + } + if (fTextSelected || fSelecting) { + InvertSelectionArea(hwnd); + } + SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); + EndPaint(hwnd, &ps); +} +#ifdef HARDDEBUG +static void dump_linebufs(void) { + char *buff; + ScreenLine_t *s = buffer_top; + fprintf(stderr,"LinebufDump------------------------\n"); + while(s) { + if (s == buffer_top) fprintf(stderr,"BT-> "); + if (s == buffer_bottom) fprintf(stderr,"BB-> "); + if (s == cur_line) fprintf(stderr,"CL-> "); + + buff = (char *) ALLOC(s->width+1); + memcpy(buff,s->text,s->width); + buff[s->width] = '\0'; + fprintf(stderr,"{\"%s\",%d,%d}\n",buff,s->newline,s->allocated); + FREE(buff); + s = s->next; + } + fprintf(stderr,"LinebufDumpEnd---------------------\n"); + fflush(stderr); +} +#endif + +static void reorganize_linebufs(HWND hwnd) { + ScreenLine_t *otop = buffer_top; + ScreenLine_t *obot = buffer_bottom; + ScreenLine_t *next; + int i,cpos; + + cpos = 0; + i = nBufLines - cur_y; + while (i > 1) { + cpos += obot->width; + obot = obot->prev; + i--; + } + cpos += (obot->width - cur_x); +#ifdef HARDDEBUG + fprintf(stderr,"nBufLines = %d, cur_x = %d, cur_y = %d, cpos = %d\n", + nBufLines,cur_x,cur_y,cpos); + fflush(stderr); +#endif + + + nBufLines = 0; + buffer_top = cur_line = ConNewLine(); + cur_line->next = buffer_bottom = ConNewLine(); + buffer_bottom->prev = cur_line; + + cur_x = cur_y = 0; + iVscrollPos = 0; + iHscrollPos = 0; + + while(otop) { + for(i=0;i<otop->width;++i) { + cur_line->text[cur_x] = otop->text[i]; + cur_x++; + if (cur_x > cur_line->width) + cur_line->width = cur_x; + if (GetXFromCurrentY(GetDC(hwnd),0,cur_x) + cxChar > + (LINE_LENGTH * cxChar)) { + ConCarriageFeed(0); + } + } + if (otop->newline) { + ConCarriageFeed(1); + /*ConScrollScreen();*/ + } + next = otop->next; + FREE(otop->text); + FREE(otop); + otop = next; + } + while (cpos) { + cur_x--; + if (cur_x < 0) { + cur_y--; + cur_line = cur_line->prev; + cur_x = cur_line->width-1; + } + cpos--; + } + SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); +#ifdef HARDDEBUG + fprintf(stderr,"canvasColumns = %d,nBufLines = %d, cur_x = %d, cur_y = %d\n", + canvasColumns,nBufLines,cur_x,cur_y); + fflush(stderr); +#endif +} + + +static void +Client_OnSize(HWND hwnd, UINT state, int cx, int cy) +{ + RECT r; + SCROLLBARINFO sbi; + int w,h,columns; + int scrollheight; + cxClient = cx; + cyClient = cy; + set_scroll_info(hwnd); + GetClientRect(hwnd,&r); + w = r.right - r.left; + h = r.bottom - r.top; + sbi.cbSize = sizeof(SCROLLBARINFO); + if (!GetScrollBarInfo(hwnd, OBJID_HSCROLL,&sbi) || + (sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)) { + scrollheight = 0; + } else { + scrollheight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; + } + canvasRows = (h - scrollheight) / cyChar; + if (canvasRows < DEF_CANVAS_ROWS) { + canvasRows = DEF_CANVAS_ROWS; + } + columns = (w - GetSystemMetrics(SM_CXVSCROLL)) /cxChar; + if (columns < DEF_CANVAS_COLUMNS) + columns = DEF_CANVAS_COLUMNS; + if (columns != canvasColumns) { + canvasColumns = columns; + /*dump_linebufs();*/ + reorganize_linebufs(hwnd); + fSelecting = fTextSelected = FALSE; + InvalidateRect(hwnd, NULL, TRUE); +#ifdef HARDDEBUG + fprintf(stderr,"Paint: cols = %d, rows = %d\n",canvasColumns,canvasRows); + fflush(stderr); +#endif + } + + SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); +} + +static void calc_charpoint_from_point(HDC dc, int x, int y, int y_offset, POINT *pt) +{ + int r; + int hscrollPix = iHscrollPos * cxChar; + + pt->y = y/cyChar + iVscrollPos + y_offset; + + if (x > (LINE_LENGTH-iHscrollPos) * cxChar) { + x = (LINE_LENGTH-iHscrollPos) * cxChar; + } + if (pt->y - y_offset > 0 && GetLineFromY(pt->y - y_offset) == NULL) { + pt->y = nBufLines - 1 + y_offset; + pt->x = GetLineFromY(pt->y - y_offset)->width; + } else { + for (pt->x = 1; + (r = GetXFromLine(dc, 0, pt->x, GetLineFromY(pt->y - y_offset))) != 0 && + (r - hscrollPix) < x; + ++(pt->x)) + ; + if ((r - hscrollPix) > x) + --(pt->x); +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"pt->x = %d, iHscrollPos = %d\n",(int) pt->x, iHscrollPos); + fflush(stderr); +#endif + if (pt->x <= 0) { + pt->x = x/cxChar + iHscrollPos; + } + } +} + + +static void +Client_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) +{ + int r; + SetFocus(GetParent(hwnd)); /* In case combobox steals the focus */ +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"OnLButtonDown fSelecting = %d, fTextSelected = %d:\n", + fSelecting,fTextSelected); + fflush(stderr); +#endif + if (fTextSelected) { + InvertSelectionArea(hwnd); + } + fTextSelected = FALSE; + + calc_charpoint_from_point(GetDC(hwnd), x, y, 0, &editBeg); + + editEnd.x = editBeg.x; + editEnd.y = editBeg.y + 1; + fSelecting = TRUE; + SetCapture(hwnd); +} + +static void +Client_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) +{ + if (fTextSelected) { + fSelecting = TRUE; + Client_OnMouseMove(hwnd,x,y,keyFlags); + fSelecting = FALSE; + } +} + +static void +Client_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags) +{ +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"OnLButtonUp fSelecting = %d, fTextSelected = %d:\n", + fSelecting,fTextSelected); + fprintf(stderr,"(Beg.x = %d, Beg.y = %d, " + "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y, + editEnd.x,editEnd.y); +#endif + if (fSelecting && + !(editBeg.x == editEnd.x && editBeg.y == (editEnd.y - 1))) { + fTextSelected = TRUE; + } +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"OnLButtonUp fTextSelected = %d:\n", + fTextSelected); + fflush(stderr); +#endif + fSelecting = FALSE; + ReleaseCapture(); +} + +#define EMPTY_RECT(R) \ +(((R).bottom - (R).top == 0) || ((R).right - (R).left == 0)) +#define ABS(X) (((X)< 0) ? -1 * (X) : X) +#define DIFF(A,B) ABS(((int)(A)) - ((int)(B))) + +static int diff_sel_area(RECT old[3], RECT new[3], RECT result[6]) +{ + int absposold = old[0].left + old[0].top * canvasColumns; + int absposnew = new[0].left + new[0].top * canvasColumns; + int absendold = absposold, absendnew = absposnew; + int i, x, ret = 0; + int abspos[2],absend[2]; + for(i = 0; i < 3; ++i) { + if (!EMPTY_RECT(old[i])) { + absendold += (old[i].right - old[i].left) * + (old[i].bottom - old[i].top); + } + if (!EMPTY_RECT(new[i])) { + absendnew += (new[i].right - new[i].left) * + (new[i].bottom - new[i].top); + } + } + abspos[0] = min(absposold, absposnew); + absend[0] = DIFF(absposold, absposnew) + abspos[0]; + abspos[1] = min(absendold, absendnew); + absend[1] = DIFF(absendold, absendnew) + abspos[1]; +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"abspos[0] = %d, absend[0] = %d, abspos[1] = %d, absend[1] = %d\n",abspos[0],absend[0],abspos[1],absend[1]); + fflush(stderr); +#endif + i = 0; + for (x = 0; x < 2; ++x) { + if (abspos[x] != absend[x]) { + int consumed = 0; + result[i].left = abspos[x] % canvasColumns; + result[i].top = abspos[x] / canvasColumns; + result[i].bottom = result[i].top + 1; + if ((absend[x] - abspos[x]) + result[i].left < canvasColumns) { +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"Nowrap, %d < canvasColumns\n", + (absend[x] - abspos[x]) + result[i].left); + fflush(stderr); +#endif + result[i].right = (absend[x] - abspos[x]) + result[i].left; + consumed += result[i].right - result[i].left; + } else { +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"Wrap, %d >= canvasColumns\n", + (absend[x] - abspos[x]) + result[i].left); + fflush(stderr); +#endif + result[i].right = canvasColumns; + consumed += result[i].right - result[i].left; + if (absend[x] - abspos[x] - consumed >= canvasColumns) { + ++i; + result[i].top = result[i-1].bottom; + result[i].left = 0; + result[i].right = canvasColumns; + result[i].bottom = (absend[x] - abspos[x] - consumed) / canvasColumns + result[i].top; + consumed += (result[i].bottom - result[i].top) * canvasColumns; + } + if (absend[x] - abspos[x] - consumed > 0) { + ++i; + result[i].top = result[i-1].bottom; + result[i].bottom = result[i].top + 1; + result[i].left = 0; + result[i].right = absend[x] - abspos[x] - consumed; + } + } + ++i; + } + } +#ifdef HARD_SEL_DEBUG + if (i > 2) { + int x; + fprintf(stderr,"i = %d\n",i); + fflush(stderr); + for (x = 0; x < i; ++x) { + fprintf(stderr, "result[%d]: top = %d, left = %d, " + "bottom = %d. right = %d\n", + x, result[x].top, result[x].left, + result[x].bottom, result[x].right); + } + } +#endif + return i; +} + + + +static void calc_sel_area(RECT rects[3], POINT beg, POINT end) +{ + /* These are not really rects and points, these are character + based positions, need to be multiplied by cxChar and cyChar to + make up canvas coordinates */ + memset(rects,0,3*sizeof(RECT)); + rects[0].left = beg.x; + rects[0].top = beg.y; + rects[0].bottom = beg.y+1; + if (end.y - beg.y == 1) { /* Only one row */ + rects[0].right = end.x; + goto out; + } + rects[0].right = canvasColumns; + if (end.y - beg.y > 2) { + rects[1].left = 0; + rects[1].top = rects[0].bottom; + rects[1].right = canvasColumns; + rects[1].bottom = end.y - 1; + } + rects[2].left = 0; + rects[2].top = end.y - 1; + rects[2].bottom = end.y; + rects[2].right = end.x; + + out: +#ifdef HARD_SEL_DEBUG + { + int i; + fprintf(stderr,"beg.x = %d, beg.y = %d, end.x = %d, end.y = %d\n", + beg.x,beg.y,end.x,end.y); + for (i = 0; i < 3; ++i) { + fprintf(stderr,"[%d] left = %d, top = %d, " + "right = %d, bottom = %d\n", + i, rects[i].left, rects[i].top, + rects[i].right, rects[i].bottom); + } + fflush(stderr); + } +#endif + return; +} + +static void calc_sel_area_turned(RECT rects[3], POINT eBeg, POINT eEnd) { + POINT from,to; + if (eBeg.y >= eEnd.y || + (eBeg.y == eEnd.y - 1 && eBeg.x > eEnd.x)) { +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"Reverting (Beg.x = %d, Beg.y = %d, " + "End.x = %d, End.y = %d)\n",eBeg.x,eBeg.y, + eEnd.x,eEnd.y); + fflush(stderr); +#endif + from.x = eEnd.x; + from.y = eEnd.y - 1; + to.x = eBeg.x; + to.y = eBeg.y + 1; + calc_sel_area(rects,from,to); + } else { + calc_sel_area(rects,eBeg,eEnd); + } +} + + +static void InvertSelectionArea(HWND hwnd) +{ + RECT rects[3]; + POINT from,to; + int i; + calc_sel_area_turned(rects,editBeg,editEnd); + for (i = 0; i < 3; ++i) { + if (!EMPTY_RECT(rects[i])) { + from.x = rects[i].left; + to.x = rects[i].right; + from.y = rects[i].top; + to.y = rects[i].bottom; + DrawSelection(hwnd,from,to); + } + } +} + +static void +Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) +{ + if (fSelecting) { + RECT rold[3], rnew[3], rupdate[6]; + int num_updates,i,r; + POINT from,to; + calc_sel_area_turned(rold,editBeg,editEnd); + + calc_charpoint_from_point(GetDC(hwnd), x, y, 1, &editEnd); + + calc_sel_area_turned(rnew,editBeg,editEnd); + num_updates = diff_sel_area(rold,rnew,rupdate); + for (i = 0; i < num_updates;++i) { + from.x = rupdate[i].left; + to.x = rupdate[i].right; + from.y = rupdate[i].top; + to.y = rupdate[i].bottom; +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"from: x=%d,y=%d, to: x=%d, y=%d\n", + from.x, from.y,to.x,to.y); + fflush(stderr); +#endif + DrawSelection(hwnd,from,to); + } + } +} + +static void +Client_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos) +{ + int iVscroll; + + switch(code) { + case SB_LINEDOWN: + iVscroll = 1; + break; + case SB_LINEUP: + iVscroll = -1; + break; + case SB_PAGEDOWN: + iVscroll = max(1, cyClient/cyChar); + break; + case SB_PAGEUP: + iVscroll = min(-1, -cyClient/cyChar); + break; + case SB_THUMBTRACK: + iVscroll = pos - iVscrollPos; + break; + default: + iVscroll = 0; + } + iVscroll = max(-iVscrollPos, min(iVscroll, iVscrollMax-iVscrollPos)); + if (iVscroll != 0) { + iVscrollPos += iVscroll; + ScrollWindowEx(hwnd, 0, -cyChar*iVscroll, NULL, NULL, + NULL, NULL, SW_ERASE | SW_INVALIDATE); + SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE); + iVscroll = GetScrollPos(hwnd, SB_VERT); + UpdateWindow(hwnd); + } +} + +static void +Client_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos) +{ + int iHscroll, curCharWidth = cxClient/cxChar; + + switch(code) { + case SB_LINEDOWN: + iHscroll = 1; + break; + case SB_LINEUP: + iHscroll = -1; + break; + case SB_PAGEDOWN: + iHscroll = max(1,curCharWidth-1); + break; + case SB_PAGEUP: + iHscroll = min(-1,-(curCharWidth-1)); + break; + case SB_THUMBTRACK: + iHscroll = pos - iHscrollPos; + break; + default: + iHscroll = 0; + } + iHscroll = max(-iHscrollPos, min(iHscroll, iHscrollMax-iHscrollPos-(curCharWidth-1))); + if (iHscroll != 0) { + iHscrollPos += iHscroll; + ScrollWindow(hwnd, -cxChar*iHscroll, 0, NULL, NULL); + SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE); + UpdateWindow(hwnd); + } +} + +static LRESULT CALLBACK +ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + switch (iMsg) { + HANDLE_MSG(hwnd, WM_CREATE, Client_OnCreate); + HANDLE_MSG(hwnd, WM_SIZE, Client_OnSize); + HANDLE_MSG(hwnd, WM_PAINT, Client_OnPaint); + HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Client_OnLButtonDown); + HANDLE_MSG(hwnd, WM_RBUTTONDOWN, Client_OnRButtonDown); + HANDLE_MSG(hwnd, WM_LBUTTONUP, Client_OnLButtonUp); + HANDLE_MSG(hwnd, WM_MOUSEMOVE, Client_OnMouseMove); + HANDLE_MSG(hwnd, WM_VSCROLL, Client_OnVScroll); + HANDLE_MSG(hwnd, WM_HSCROLL, Client_OnHScroll); + case WM_CONBEEP: + if (0) Beep(440, 400); + return 0; + case WM_CONTEXT: + ConDrawText(hwnd); + return 0; + case WM_CLOSE: + break; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc (hwnd, iMsg, wParam, lParam); +} + +static void +LoadUserPreferences(void) +{ + DWORD size; + DWORD res; + DWORD type; + + /* default prefs */ + GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont); + fgColor = GetSysColor(COLOR_WINDOWTEXT); + bkgColor = GetSysColor(COLOR_WINDOW); + winPos.left = -1; + toolbarVisible = TRUE; + + if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, + &key, &res) != ERROR_SUCCESS) + return; + has_key = TRUE; + if (res == REG_CREATED_NEW_KEY) + return; + size = sizeof(logfont); + res = RegQueryValueEx(key,TEXT("Font"),NULL,&type,(LPBYTE)&logfont,&size); + size = sizeof(fgColor); + res = RegQueryValueEx(key,TEXT("FgColor"),NULL,&type,(LPBYTE)&fgColor,&size); + size = sizeof(bkgColor); + res = RegQueryValueEx(key,TEXT("BkColor"),NULL,&type,(LPBYTE)&bkgColor,&size); + size = sizeof(winPos); + res = RegQueryValueEx(key,TEXT("Pos"),NULL,&type,(LPBYTE)&winPos,&size); + size = sizeof(toolbarVisible); + res = RegQueryValueEx(key,TEXT("Toolbar"),NULL,&type,(LPBYTE)&toolbarVisible,&size); +} + +static void +SaveUserPreferences(void) +{ + WINDOWPLACEMENT wndPlace; + + if (has_key == TRUE) { + RegSetValueEx(key,TEXT("Font"),0,REG_BINARY,(CONST BYTE *)&logfont,sizeof(LOGFONT)); + RegSetValueEx(key,TEXT("FgColor"),0,REG_DWORD,(CONST BYTE *)&fgColor,sizeof(fgColor)); + RegSetValueEx(key,TEXT("BkColor"),0,REG_DWORD,(CONST BYTE *)&bkgColor,sizeof(bkgColor)); + RegSetValueEx(key,TEXT("Toolbar"),0,REG_DWORD,(CONST BYTE *)&toolbarVisible,sizeof(toolbarVisible)); + + wndPlace.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(hFrameWnd,&wndPlace); + /* If wndPlace.showCmd == SW_MINIMIZE, then the window is minimized. + We don't care, wndPlace.rcNormalPosition always holds the last known position. */ + winPos = wndPlace.rcNormalPosition; + RegSetValueEx(key,TEXT("Pos"),0,REG_BINARY,(CONST BYTE *)&winPos,sizeof(winPos)); + } +} + + +static void +set_scroll_info(HWND hwnd) +{ + SCROLLINFO info; + int hScrollBy; + /* + * Set vertical scrolling range and scroll box position. + */ + + iVscrollMax = nBufLines-1; + iVscrollPos = min(iVscrollPos, iVscrollMax); + info.cbSize = sizeof(info); + info.fMask = SIF_PAGE|SIF_RANGE|SIF_POS; + info.nMin = 0; + info.nPos = iVscrollPos; + info.nPage = min(cyClient/cyChar, iVscrollMax); + info.nMax = iVscrollMax; + SetScrollInfo(hwnd, SB_VERT, &info, TRUE); + + /* + * Set horizontal scrolling range and scroll box position. + */ + + iHscrollMax = LINE_LENGTH-1; + hScrollBy = max(0, (iHscrollPos - (iHscrollMax-cxClient/cxChar))*cxChar); + iHscrollPos = min(iHscrollPos, iHscrollMax); + info.nPos = iHscrollPos; + info.nPage = cxClient/cxChar; + info.nMax = iHscrollMax; + SetScrollInfo(hwnd, SB_HORZ, &info, TRUE); + /*ScrollWindow(hwnd, hScrollBy, 0, NULL, NULL);*/ +} + + +static void +ensure_line_below(void) +{ + if (cur_line->next == NULL) { + if (nBufLines >= lines_to_save) { + ScreenLine_t* pLine = buffer_top->next; + FREE(buffer_top->text); + FREE(buffer_top); + buffer_top = pLine; + buffer_top->prev = NULL; + nBufLines--; + } + cur_line->next = ConNewLine(); + cur_line->next->prev = cur_line; + buffer_bottom = cur_line->next; + set_scroll_info(hClientWnd); + } +} + +static ScreenLine_t* +ConNewLine(void) +{ + ScreenLine_t *pLine; + + pLine = (ScreenLine_t *)ALLOC(sizeof(ScreenLine_t)); + if (!pLine) + return NULL; + pLine->text = (TCHAR *) ALLOC(canvasColumns * sizeof(TCHAR)); +#ifdef HARDDEBUG + pLine->allocated = canvasColumns; +#endif + pLine->width = 0; + pLine->prev = pLine->next = NULL; + pLine->newline = 0; + nBufLines++; + return pLine; +} + +static ScreenLine_t* +GetLineFromY(int y) +{ + ScreenLine_t *pLine = buffer_top; + int i; + + for (i = 0; i < nBufLines && pLine != NULL; i++) { + if (i == y) + return pLine; + pLine = pLine->next; + } + return NULL; +} + +void ConCarriageFeed(int hard_newline) +{ + cur_x = 0; + ensure_line_below(); + cur_line->newline = hard_newline; + cur_line = cur_line->next; + if (cur_y < nBufLines-1) { + cur_y++; + } else if (iVscrollPos > 0) { + iVscrollPos--; + } +} + +/* + * Scroll screen if cursor is not visible. + */ +static void +ConScrollScreen(void) +{ + if (cur_y >= iVscrollPos + cyClient/cyChar) { + int iVscroll; + + iVscroll = cur_y - iVscrollPos - cyClient/cyChar + 1; + iVscrollPos += iVscroll; + ScrollWindowEx(hClientWnd, 0, -cyChar*iVscroll, NULL, NULL, + NULL, NULL, SW_ERASE | SW_INVALIDATE); + SetScrollPos(hClientWnd, SB_VERT, iVscrollPos, TRUE); + UpdateWindow(hClientWnd); + } +} + +static void +DrawSelection(HWND hwnd, POINT pt1, POINT pt2) +{ + HDC hdc; + int width,height; +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n", + (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y); +#endif + pt1.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt1.x,GetLineFromY(pt1.y)); + pt2.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt2.x,GetLineFromY(pt2.y-1)); + pt1.y -= iVscrollPos; + pt2.y -= iVscrollPos; + pt1.y *= cyChar; + pt2.y *= cyChar; +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n", + (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y); + fflush(stderr); +#endif + width = pt2.x-pt1.x; + height = pt2.y - pt1.y; + hdc = GetDC(hwnd); + PatBlt(hdc,pt1.x,pt1.y,width,height,DSTINVERT); + ReleaseDC(hwnd,hdc); +} + +static void +OnEditCopy(HWND hwnd) +{ + HGLOBAL hMem; + TCHAR *pMem; + ScreenLine_t *pLine; + RECT rects[3]; + POINT from,to; + int i,j,sum,len; + if (editBeg.y >= editEnd.y || + (editBeg.y == editEnd.y - 1 && editBeg.x > editEnd.x)) { +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"CopyReverting (Beg.x = %d, Beg.y = %d, " + "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y, + editEnd.x,editEnd.y); + fflush(stderr); +#endif + from.x = editEnd.x; + from.y = editEnd.y - 1; + to.x = editBeg.x; + to.y = editBeg.y + 1; + calc_sel_area(rects,from,to); + } else { + calc_sel_area(rects,editBeg,editEnd); + } + sum = 1; + for (i = 0; i < 3; ++i) { + if (!EMPTY_RECT(rects[i])) { + pLine = GetLineFromY(rects[i].top); + for (j = rects[i].top; j < rects[i].bottom ;++j) { + if (pLine == NULL) { + sum += 2; + break; + } + if (pLine->width > rects[i].left) { + sum += (pLine->width < rects[i].right) ? + pLine->width - rects[i].left : + rects[i].right - rects[i].left; + } + if(pLine->newline && rects[i].right >= pLine->width) { + sum += 2; + } + pLine = pLine->next; + } + } + } +#ifdef HARD_SEL_DEBUG + fprintf(stderr,"sum = %d\n",sum); + fflush(stderr); +#endif + hMem = GlobalAlloc(GHND, sum * sizeof(TCHAR)); + pMem = GlobalLock(hMem); + for (i = 0; i < 3; ++i) { + if (!EMPTY_RECT(rects[i])) { + pLine = GetLineFromY(rects[i].top); + for (j = rects[i].top; j < rects[i].bottom; ++j) { + if (pLine == NULL) { + memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR)); + pMem += 2; + break; + } + if (pLine->width > rects[i].left) { + len = (pLine->width < rects[i].right) ? + pLine->width - rects[i].left : + rects[i].right - rects[i].left; + memcpy(pMem,pLine->text + rects[i].left,len * sizeof(TCHAR)); + pMem +=len; + } + if(pLine->newline && rects[i].right >= pLine->width) { + memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR)); + pMem += 2; + } + pLine = pLine->next; + } + } + } + *pMem = TEXT('\0'); + /* Flash de selection area to give user feedback about copying */ + InvertSelectionArea(hwnd); + Sleep(100); + InvertSelectionArea(hwnd); + + OpenClipboard(hwnd); + EmptyClipboard(); + GlobalUnlock(hMem); + SetClipboardData(CF_UNICODETEXT,hMem); + CloseClipboard(); +} + +/* XXX:PaN Tchar or char? */ +static void +OnEditPaste(HWND hwnd) +{ + HANDLE hClipMem; + TCHAR *pClipMem,*pMem,*pMem2; + if (!OpenClipboard(hwnd)) + return; + if ((hClipMem = GetClipboardData(CF_UNICODETEXT)) != NULL) { + pClipMem = GlobalLock(hClipMem); + pMem = (TCHAR *)ALLOC(GlobalSize(hClipMem) * sizeof(TCHAR)); + pMem2 = pMem; + while ((*pMem2 = *pClipMem) != TEXT('\0')) { + if (*pClipMem == TEXT('\r')) + *pMem2 = TEXT('\n'); + ++pMem2; + ++pClipMem; + } + GlobalUnlock(hClipMem); + write_inbuf(pMem, _tcsclen(pMem)); + } + CloseClipboard(); +} + +static void +OnEditSelAll(HWND hwnd) +{ + editBeg.x = 0; + editBeg.y = 0; + editEnd.x = LINE_LENGTH-1; + editEnd.y = cur_y; + fTextSelected = TRUE; + InvalidateRect(hwnd, NULL, TRUE); +} + +UINT APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) +{ + /* Hook procedure for font dialog box */ + HWND hOwner; + RECT rc,rcOwner,rcDlg; + switch (iMsg) { + case WM_INITDIALOG: + /* center dialogbox within its owner window */ + if ((hOwner = GetParent(hDlg)) == NULL) + hOwner = GetDesktopWindow(); + GetWindowRect(hOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); + return 1; + default: + break; + } + return 0; /* Let the default procedure process the message */ +} + +static BOOL +ConChooseFont(HWND hwnd) +{ + HDC hdc; + hdc = GetDC(hwnd); + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = hwnd; + cf.hDC = NULL; + cf.lpLogFont = &logfont; + cf.iPointSize = 0; + cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_SCREENFONTS|CF_FIXEDPITCHONLY|CF_EFFECTS|CF_ENABLEHOOK; + cf.rgbColors = GetTextColor(hdc); + cf.lCustData = 0L; + cf.lpfnHook = CFHookProc; + cf.lpTemplateName = NULL; + cf.hInstance = NULL; + cf.lpszStyle = NULL; + cf.nFontType = 0; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + ReleaseDC(hwnd,hdc); + return ChooseFont(&cf); +} + +static void +ConFontInitialize(HWND hwnd) +{ + HDC hdc; + TEXTMETRIC tm; + HFONT hFont; + + hFont = CreateFontIndirect(&logfont); + hdc = GetDC(hwnd); + SelectObject(hdc, hFont); + SetTextColor(hdc,fgColor); + SetBkColor(hdc,bkgColor); + GetTextMetrics(hdc, &tm); + cxChar = tm.tmAveCharWidth; + cxCharMax = tm.tmMaxCharWidth; + cyChar = tm.tmHeight + tm.tmExternalLeading; + ReleaseDC(hwnd, hdc); +} + +static void +ConSetFont(HWND hwnd) +{ + HDC hdc; + TEXTMETRIC tm; + HFONT hFontNew; + + hFontNew = CreateFontIndirect(&logfont); + SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew, + MAKELPARAM(1,0)); + hdc = GetDC(hwnd); + DeleteObject(SelectObject(hdc, hFontNew)); + GetTextMetrics(hdc, &tm); + cxChar = tm.tmAveCharWidth; + cxCharMax = tm.tmMaxCharWidth; + cyChar = tm.tmHeight + tm.tmExternalLeading; + fgColor = cf.rgbColors; + SetTextColor(hdc,fgColor); + ReleaseDC(hwnd, hdc); + set_scroll_info(hwnd); + HideCaret(hwnd); + if (DestroyCaret()) { + CreateCaret(hwnd, NULL, cxChar, cyChar); + SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); + } + ShowCaret(hwnd); + InvalidateRect(hwnd, NULL, TRUE); +} + +UINT APIENTRY +CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) +{ + /* Hook procedure for choose color dialog box */ + HWND hOwner; + RECT rc,rcOwner,rcDlg; + switch (iMsg) { + case WM_INITDIALOG: + /* center dialogbox within its owner window */ + if ((hOwner = GetParent(hDlg)) == NULL) + hOwner = GetDesktopWindow(); + GetWindowRect(hOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); + return 1; + default: + break; + } + return 0; /* Let the default procedure process the message */ +} + +void ConChooseColor(HWND hwnd) +{ + CHOOSECOLOR cc; + static COLORREF acrCustClr[16]; + HBRUSH hbrush; + HDC hdc; + + /* Initialize CHOOSECOLOR */ + ZeroMemory(&cc, sizeof(CHOOSECOLOR)); + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = hwnd; + cc.lpCustColors = (LPDWORD) acrCustClr; + cc.rgbResult = bkgColor; + cc.lpfnHook = CCHookProc; + cc.Flags = CC_FULLOPEN|CC_RGBINIT|CC_SOLIDCOLOR|CC_ENABLEHOOK; + + if (ChooseColor(&cc)==TRUE) { + bkgColor = cc.rgbResult; + hdc = GetDC(hwnd); + SetBkColor(hdc,bkgColor); + ReleaseDC(hwnd,hdc); + hbrush = CreateSolidBrush(bkgColor); + DeleteObject((HBRUSH)SetClassLong(hClientWnd,GCL_HBRBACKGROUND,(LONG)hbrush)); + InvalidateRect(hwnd,NULL,TRUE); + } +} + +UINT APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,WPARAM wParam,LPARAM lParam) +{ + /* Hook procedure for open file dialog box */ + HWND hOwner,hDlg; + RECT rc,rcOwner,rcDlg; + hDlg = GetParent(hwndDlg); + switch (iMsg) { + case WM_INITDIALOG: + /* center dialogbox within its owner window */ + if ((hOwner = GetParent(hDlg)) == NULL) + hOwner = GetDesktopWindow(); + GetWindowRect(hOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); + return 1; + default: + break; + } + return 0; /* the let default procedure process the message */ +} + +static void +GetFileName(HWND hwnd, TCHAR *pFile) +{ + /* Open the File Open dialog box and */ + /* retrieve the file name */ + OPENFILENAME ofn; + TCHAR szFilterSpec [128] = TEXT("logfiles (*.log)\0*.log\0All files (*.*)\0*.*\0\0"); + #define MAXFILENAME 256 + TCHAR szFileName[MAXFILENAME]; + TCHAR szFileTitle[MAXFILENAME]; + + /* these need to be filled in */ + _tcscpy(szFileName, TEXT("erlshell.log")); + _tcscpy(szFileTitle, TEXT("")); /* must be NULL */ + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = NULL; + ofn.lpstrFilter = szFilterSpec; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 0; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAXFILENAME; + ofn.lpstrInitialDir = NULL; + ofn.lpstrFileTitle = szFileTitle; + ofn.nMaxFileTitle = MAXFILENAME; + ofn.lpstrTitle = TEXT("Open logfile"); + ofn.lpstrDefExt = TEXT("log"); + ofn.Flags = OFN_CREATEPROMPT|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_ENABLEHOOK|OFN_NOCHANGEDIR; /* OFN_NOCHANGEDIR only works in Vista :( */ + ofn.lpfnHook = OFNHookProc; + + if (!GetOpenFileName ((LPOPENFILENAME)&ofn)){ + *pFile = TEXT('\0'); + } else { + _tcscpy(pFile, ofn.lpstrFile); + } +} + +void OpenLogFile(HWND hwnd) +{ + /* open a file for logging */ + TCHAR filename[_MAX_PATH]; + + GetFileName(hwnd, filename); + if (filename[0] == '\0') + return; + if (NULL == (logfile = _tfopen(filename,TEXT("w,ccs=UNICODE")))) + return; +} + +void CloseLogFile(HWND hwnd) +{ + /* close log file */ + fclose(logfile); + logfile = NULL; +} + +void LogFileWrite(TCHAR *buf, int num_chars) +{ + /* write to logfile */ + int from,to; + while (num_chars-- > 0) { + switch (*buf) { + case SET_CURSOR: + buf++; + from = *((int *)buf); + buf += sizeof(int)/sizeof(TCHAR); + to = *((int *)buf); + buf += (sizeof(int)/sizeof(TCHAR))-1; + num_chars -= 2 * (sizeof(int)/sizeof(TCHAR)); + // Wont seek in Unicode file, sorry... + // fseek(logfile,to-from *sizeof(TCHAR),SEEK_CUR); + break; + default: + _fputtc(*buf,logfile); + break; + } + buf++; + } +} + +static void +init_buffers(void) +{ + inbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR)); + outbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR)); + inbuf.size = BUFSIZE; + inbuf.rdPos = inbuf.wrPos = 0; + outbuf.size = BUFSIZE; + outbuf.rdPos = outbuf.wrPos = 0; +} + +static int +check_realloc(buffer_t *buf, int num_chars) +{ + if (buf->wrPos + num_chars >= buf->size) { + if (buf->size > MAXBUFSIZE) + return 0; + buf->size += num_chars + BUFSIZE; + if (!(buf->data = (TCHAR *)REALLOC(buf->data, buf->size * sizeof(TCHAR)))) { + buf->size = buf->rdPos = buf->wrPos = 0; + return 0; + } + } + return 1; +} + +static int +write_inbuf(TCHAR *data, int num_chars) +{ + TCHAR *buf; + int nwrite; + WaitForSingleObject(console_input,INFINITE); + if (!check_realloc(&inbuf,num_chars)) { + ReleaseSemaphore(console_input,1,NULL); + return -1; + } + buf = &inbuf.data[inbuf.wrPos]; + inbuf.wrPos += num_chars; + nwrite = num_chars; + while (nwrite--) + *buf++ = *data++; + SetEvent(console_input_event); + ReleaseSemaphore(console_input,1,NULL); + return num_chars; +} + +static int +write_outbuf(TCHAR *data, int num_chars) +{ + TCHAR *buf; + int nwrite; + + WaitForSingleObject(console_output,INFINITE); + if (!check_realloc(&outbuf, num_chars)) { + ReleaseSemaphore(console_output,1,NULL); + return -1; + } + if (outbuf.rdPos == outbuf.wrPos) + PostMessage(hClientWnd, WM_CONTEXT, 0L, 0L); + buf = &outbuf.data[outbuf.wrPos]; + outbuf.wrPos += num_chars; + nwrite = num_chars; + while (nwrite--) + *buf++ = *data++; + ReleaseSemaphore(console_output,1,NULL); + return num_chars; +} + +BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + HWND hOwner; + RECT rc,rcOwner,rcDlg; + + switch (iMsg) { + case WM_INITDIALOG: + /* center dialogbox within its owner window */ + if ((hOwner = GetParent(hDlg)) == NULL) + hOwner = GetDesktopWindow(); + GetWindowRect(hOwner, &rcOwner); + GetWindowRect(hDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE); + SetDlgItemText(hDlg, ID_VERSIONSTRING, + TEXT("Erlang emulator version ") TEXT(ERLANG_VERSION)); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + EndDialog(hDlg,0); + return TRUE; + } + break; + } + return FALSE; +} + +static void +ConDrawText(HWND hwnd) +{ + int num_chars; + int nchars; + TCHAR *buf; + int from, to; + int dl; + int dc; + RECT rc; + + WaitForSingleObject(console_output, INFINITE); + nchars = 0; + num_chars = outbuf.wrPos - outbuf.rdPos; + buf = &outbuf.data[outbuf.rdPos]; + if (logfile != NULL) + LogFileWrite(buf, num_chars); + + +#ifdef HARDDEBUG + { + TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR)); + memcpy(bu,buf,num_chars * sizeof(TCHAR)); + bu[num_chars]='\0'; + fprintf(stderr,TEXT("ConDrawText\"%s\"\n"),bu); + FREE(bu); + fflush(stderr); + } +#endif + /* + * Don't draw any text in the window; just update the line buffers + * and invalidate the appropriate part of the window. The window + * will be updated on the next WM_PAINT message. + */ + + while (num_chars-- > 0) { + switch (*buf) { + case '\r': + break; + case '\n': + if (nchars > 0) { + rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars); + rc.right = rc.left + cxCharMax*nchars; + rc.top = cyChar * (cur_y-iVscrollPos); + rc.bottom = rc.top + cyChar; + InvalidateRect(hwnd, &rc, TRUE); + nchars = 0; + } + ConCarriageFeed(1); + ConScrollScreen(); + break; + case SET_CURSOR: + if (nchars > 0) { + rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars); + rc.right = rc.left + cxCharMax*nchars; + rc.top = cyChar * (cur_y-iVscrollPos); + rc.bottom = rc.top + cyChar; + InvalidateRect(hwnd, &rc, TRUE); + nchars = 0; + } + buf++; + from = *((int *)buf); + buf += sizeof(int)/sizeof(TCHAR); + to = *((int *)buf); + buf += (sizeof(int)/sizeof(TCHAR))-1; + num_chars -= 2 * (sizeof(int)/sizeof(TCHAR)); + while (to > from) { + cur_x++; + if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar > + (LINE_LENGTH * cxChar)) { + cur_x = 0; + cur_y++; + ensure_line_below(); + cur_line = cur_line->next; + } + from++; + } + while (to < from) { + cur_x--; + if (cur_x < 0) { + cur_y--; + cur_line = cur_line->prev; + cur_x = cur_line->width-1; + } + from--; + } + + break; + default: + nchars++; + cur_line->text[cur_x] = *buf; + cur_x++; + if (cur_x > cur_line->width) + cur_line->width = cur_x; + if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar > + (LINE_LENGTH * cxChar)) { + if (nchars > 0) { + rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars); + rc.right = rc.left + cxCharMax*nchars; + rc.top = cyChar * (cur_y-iVscrollPos); + rc.bottom = rc.top + cyChar; + InvalidateRect(hwnd, &rc, TRUE); + } + ConCarriageFeed(0); + nchars = 0; + } + } + buf++; + } + if (nchars > 0) { + rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars); + rc.right = rc.left + cxCharMax*nchars; + rc.top = cyChar * (cur_y-iVscrollPos); + rc.bottom = rc.top + cyChar; + InvalidateRect(hwnd, &rc, TRUE); + } + ConScrollScreen(); + SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar); + outbuf.wrPos = outbuf.rdPos = 0; + ReleaseSemaphore(console_output, 1, NULL); +} + +static void +AddToCmdHistory(void) +{ + int i; + int size; + Uint32 *buf; + wchar_t cmdBuf[128]; + + if (llen != 0) { + for (i = 0, size = 0; i < llen-1; i++) { + /* + * Find end of prompt. + */ + if ((lbuf[i] == '>') && lbuf[i+1] == ' ') { + buf = &lbuf[i+2]; + size = llen-i-2; + break; + } + } + if (size > 0 && size < 128) { + for (i = 0;i < size; ++i) { + cmdBuf[i] = (wchar_t) buf[i]; + } + cmdBuf[size] = 0; + SendMessage(hComboWnd,CB_INSERTSTRING,0,(LPARAM)cmdBuf); + } + } +} + +static TBBUTTON tbb[] = +{ + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, + 0, IDMENU_COPY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, + 1, IDMENU_PASTE, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, + 2, IDMENU_FONT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, + 3, IDMENU_ABOUT, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0, + 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0, 0, +}; + +static TBADDBITMAP tbbitmap = +{ + HINST_COMMCTRL, IDB_STD_SMALL_COLOR, +}; + + +static HWND +InitToolBar(HWND hwndParent) +{ + int x,y,cx; + HWND hwndTB,hwndTT; + RECT r; + TOOLINFO ti; + HFONT hFontNew; + DWORD backgroundColor = GetSysColor(COLOR_BTNFACE); + COLORMAP colorMap; + colorMap.from = RGB(192, 192, 192); + colorMap.to = backgroundColor; + + /* Create toolbar window with tooltips */ + hwndTB = CreateWindowEx(0,TOOLBARCLASSNAME,(TCHAR *)NULL, + WS_CHILD|CCS_TOP|WS_CLIPSIBLINGS|TBSTYLE_TOOLTIPS, + 0,0,0,0,hwndParent, + (HMENU)2,hInstance,NULL); + SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE, + (WPARAM) sizeof(TBBUTTON),0); + tbbitmap.hInst = NULL; + tbbitmap.nID = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1); + SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4, + (WPARAM) &tbbitmap); + SendMessage(hwndTB,TB_ADDBUTTONS, (WPARAM) 30, + (LPARAM) (LPTBBUTTON) tbb); + if (toolbarVisible) + ShowWindow(hwndTB, SW_SHOW); + + /* Create combobox window */ + SendMessage(hwndTB,TB_GETITEMRECT,0,(LPARAM)&r); + x = r.left; y = r.top; + SendMessage(hwndTB,TB_GETITEMRECT,23,(LPARAM)&r); + cx = r.right - x + 1; + hComboWnd = CreateWindow(TEXT("combobox"),NULL,WS_VSCROLL|WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST, + x,y,cx,100,hwndParent,(HMENU)ID_COMBOBOX, hInstance,NULL); + SetParent(hComboWnd,hwndTB); + hFontNew = CreateFontIndirect(&logfont); + SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew, + MAKELPARAM(1,0)); + + /* Add tooltip for combo box */ + ZeroMemory(&ti,sizeof(TOOLINFO)); + ti.cbSize = sizeof(TOOLINFO); + ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS; + ti.hwnd = hwndTB;; + ti.uId = (UINT)hComboWnd; + ti.lpszText = LPSTR_TEXTCALLBACK; + hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0); + SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti); + + return hwndTB; +} + +static void +window_title(struct title_buf *tbuf) +{ + int res, i; + size_t bufsz = TITLE_BUF_SZ; + unsigned char charbuff[TITLE_BUF_SZ]; + + res = erl_drv_getenv("ERL_WINDOW_TITLE", charbuff, &bufsz); + if (res < 0) + tbuf->name = erlang_window_title; + else if (res == 0) { + for (i = 0; i < bufsz; ++i) { + tbuf->buf[i] = charbuff[i]; + } + tbuf->buf[bufsz - 1] = 0; + tbuf->name = &tbuf->buf[0]; + } else { + char *buf = ALLOC(bufsz); + if (!buf) + tbuf->name = erlang_window_title; + else { + while (1) { + char *newbuf; + res = erl_drv_getenv("ERL_WINDOW_TITLE", buf, &bufsz); + if (res <= 0) { + if (res == 0) { + TCHAR *wbuf = ALLOC(bufsz *sizeof(TCHAR)); + for (i = 0; i < bufsz ; ++i) { + wbuf[i] = buf[i]; + } + wbuf[bufsz - 1] = 0; + FREE(buf); + tbuf->name = wbuf; + } else { + tbuf->name = erlang_window_title; + FREE(buf); + } + break; + } + newbuf = REALLOC(buf, bufsz); + if (newbuf) + buf = newbuf; + else { + tbuf->name = erlang_window_title; + FREE(buf); + break; + } + } + } + } +} + +static void +free_window_title(struct title_buf *tbuf) +{ + if (tbuf->name != erlang_window_title && tbuf->name != &tbuf->buf[0]) + FREE(tbuf->name); +} |