aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/win32/win_con.c
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/drivers/win32/win_con.c
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/drivers/win32/win_con.c')
-rw-r--r--erts/emulator/drivers/win32/win_con.c2259
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);
+}