1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1997-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #define UNICODE 1
22 #define _UNICODE 1
23 #include <tchar.h>
24 #include <stdio.h>
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28 #include "sys.h"
29 #include <windowsx.h>
30 #include "resource.h"
31 #include "erl_version.h"
32 #include <commdlg.h>
33 #include <commctrl.h>
34 #include "erl_driver.h"
35 #include "win_con.h"
36 
37 #define ALLOC(X) malloc(X)
38 #define REALLOC(X,Y) realloc(X,Y)
39 #define FREE(X) free(X)
40 
41 #if SIZEOF_VOID_P == 8
42 #define WIN64 1
43 #ifndef GCL_HBRBACKGROUND
44 #define GCL_HBRBACKGROUND GCLP_HBRBACKGROUND
45 #endif
46 #define DIALOG_PROC_RET INT_PTR
47 #define CF_HOOK_RET INT_PTR
48 #define CC_HOOK_RET INT_PTR
49 #define OFN_HOOK_RET INT_PTR
50 #else
51 #define DIALOG_PROC_RET BOOL
52 #define CF_HOOK_RET UINT
53 #define CC_HOOK_RET UINT
54 #define OFN_HOOK_RET UINT
55 #endif
56 
57 
58 #ifndef STATE_SYSTEM_INVISIBLE
59 /* Mingw problem with oleacc.h and WIN32_LEAN_AND_MEAN */
60 #define STATE_SYSTEM_INVISIBLE 0x00008000
61 #endif
62 
63 #define WM_CONTEXT      (0x0401)
64 #define WM_CONBEEP      (0x0402)
65 #define WM_SAVE_PREFS   (0x0403)
66 
67 #define USER_KEY TEXT("Software\\Ericsson\\Erlang\\") TEXT(ERLANG_VERSION)
68 
69 #define FRAME_HEIGHT ((2*GetSystemMetrics(SM_CYEDGE))+(2*GetSystemMetrics(SM_CYFRAME))+GetSystemMetrics(SM_CYCAPTION))
70 #define FRAME_WIDTH  (2*GetSystemMetrics(SM_CXFRAME)+(2*GetSystemMetrics(SM_CXFRAME))+GetSystemMetrics(SM_CXVSCROLL))
71 
72 #define LINE_LENGTH canvasColumns
73 #define COL(_l) ((_l) % LINE_LENGTH)
74 #define LINE(_l) ((_l) / LINE_LENGTH)
75 
76 #ifdef UNICODE
77 /*
78  * We use a character in the invalid unicode range
79  */
80 #define SET_CURSOR (0xD8FF)
81 #else
82 /*
83  * XXX There is no escape to send a character 0x80.  Fortunately,
84  * the ttsl driver currently replaces 0x80 with an octal sequence.
85  */
86 #define SET_CURSOR (0x80)
87 #endif
88 
89 #define SCAN_CODE_BREAK 0x46	/* scan code for Ctrl-Break */
90 
91 
92 typedef struct ScreenLine_s {
93     struct ScreenLine_s* next;
94     struct ScreenLine_s* prev;
95     int width;
96 #ifdef HARDDEBUG
97     int allocated;
98 #endif
99     int newline; /* Ends with hard newline: 1, wrapped at end: 0 */
100     TCHAR *text;
101 } ScreenLine_t;
102 
103 extern Uint32 *lbuf;		/* The current line buffer */
104 extern int llen;		/* The current line length */
105 extern int lpos;
106 
107 HANDLE console_input_event;
108 HANDLE console_thread = NULL;
109 
110 #define DEF_CANVAS_COLUMNS 80
111 #define DEF_CANVAS_ROWS 26
112 
113 #define BUFSIZE 4096
114 #define MAXBUFSIZE 32768
115 typedef struct {
116     TCHAR *data;
117     int size;
118     int wrPos;
119     int rdPos;
120 } buffer_t;
121 
122 static buffer_t inbuf;
123 static buffer_t outbuf;
124 
125 static CHOOSEFONT cf;
126 
127 static TCHAR szFrameClass[] = TEXT("FrameClass");
128 static TCHAR szClientClass[] = TEXT("ClientClass");
129 static HWND hFrameWnd;
130 static HWND hClientWnd;
131 static HWND hTBWnd;
132 static HWND hComboWnd;
133 static HANDLE console_input;
134 static HANDLE console_output;
135 static int cxChar,cyChar, cxCharMax;
136 static int cxClient,cyClient;
137 static int cyToolBar;
138 static int iVscrollPos,iHscrollPos;
139 static int iVscrollMax,iHscrollMax;
140 static int nBufLines;
141 static int cur_x;
142 static int cur_y;
143 static int canvasColumns = DEF_CANVAS_COLUMNS;
144 static int canvasRows = DEF_CANVAS_ROWS;
145 static ScreenLine_t *buffer_top,*buffer_bottom;
146 static ScreenLine_t* cur_line;
147 static POINT editBeg,editEnd;
148 static BOOL fSelecting = FALSE;
149 static BOOL fTextSelected = FALSE;
150 static HKEY key;
151 static BOOL has_key = FALSE;
152 static LOGFONT logfont;
153 static DWORD fgColor;
154 static DWORD bkgColor;
155 static FILE *logfile = NULL;
156 static RECT winPos;
157 static BOOL toolbarVisible;
158 static BOOL destroyed = FALSE;
159 
160 static int lines_to_save = 1000; /* Maximum number of screen lines to save. */
161 
162 #define TITLE_BUF_SZ 256
163 
164 struct title_buf {
165     TCHAR *name;
166     TCHAR buf[TITLE_BUF_SZ];
167 };
168 
169 static TCHAR *erlang_window_title = TEXT("Erlang");
170 
171 static unsigned __stdcall ConThreadInit(LPVOID param);
172 static LRESULT CALLBACK ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
173 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
174 static DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam);
175 static ScreenLine_t *ConNewLine(void);
176 static void DeleteTopLine(void);
177 static void ensure_line_below(void);
178 static ScreenLine_t *GetLineFromY(int y);
179 static void LoadUserPreferences(void);
180 static void SaveUserPreferences(void);
181 static void set_scroll_info(HWND hwnd);
182 static void ConCarriageFeed(int);
183 static void ConScrollScreen(void);
184 static BOOL ConChooseFont(HWND hwnd);
185 static void ConFontInitialize(HWND hwnd);
186 static void ConSetFont(HWND hwnd);
187 static void ConChooseColor(HWND hwnd);
188 static void DrawSelection(HWND hwnd, POINT pt1, POINT pt2);
189 static void InvertSelectionArea(HWND hwnd);
190 static void OnEditCopy(HWND hwnd);
191 static void OnEditPaste(HWND hwnd);
192 static void OnEditSelAll(HWND hwnd);
193 static void GetFileName(HWND hwnd, TCHAR *pFile);
194 static void OpenLogFile(HWND hwnd);
195 static void CloseLogFile(HWND hwnd);
196 static void LogFileWrite(TCHAR *buf, int n);
197 static int write_inbuf(TCHAR *data, int n);
198 static void init_buffers(void);
199 static void AddToCmdHistory(void);
200 static int write_outbuf(TCHAR *data, int num_chars);
201 static void ConDrawText(HWND hwnd);
202 static BOOL (WINAPI *ctrl_handler)(DWORD);
203 static HWND InitToolBar(HWND hwndParent);
204 static void window_title(struct title_buf *);
205 static void free_window_title(struct title_buf *);
206 static void Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
207 
208 #define CON_VPRINTF_BUF_INC_SIZE 1024
209 
210 static erts_dsprintf_buf_t *
grow_con_vprintf_buf(erts_dsprintf_buf_t * dsbufp,size_t need)211 grow_con_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need)
212 {
213     char *buf;
214     size_t size;
215 
216     ASSERT(dsbufp);
217 
218     if (!dsbufp->str) {
219 	size = (((need + CON_VPRINTF_BUF_INC_SIZE - 1)
220 		 / CON_VPRINTF_BUF_INC_SIZE)
221 		* CON_VPRINTF_BUF_INC_SIZE);
222 	buf = (char *) ALLOC(size * sizeof(char));
223     }
224     else {
225 	size_t free_size = dsbufp->size - dsbufp->str_len;
226 
227 	if (need <= free_size)
228 	    return dsbufp;
229 
230 	size = need - free_size + CON_VPRINTF_BUF_INC_SIZE;
231 	size = (((size + CON_VPRINTF_BUF_INC_SIZE - 1)
232 		 / CON_VPRINTF_BUF_INC_SIZE)
233 		* CON_VPRINTF_BUF_INC_SIZE);
234 	size += dsbufp->size;
235 	buf = (char *) REALLOC((void *) dsbufp->str,
236 			       size * sizeof(char));
237     }
238     if (!buf)
239 	return NULL;
240     if (buf != dsbufp->str)
241 	dsbufp->str = buf;
242     dsbufp->size = size;
243     return dsbufp;
244 }
245 
con_vprintf(char * format,va_list arg_list)246 static int con_vprintf(char *format, va_list arg_list)
247 {
248     int res,i;
249     erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_con_vprintf_buf);
250     res = erts_vdsprintf(&dsbuf, format, arg_list);
251     if (res >= 0) {
252 	TCHAR *tmp = ALLOC(dsbuf.str_len*sizeof(TCHAR));
253 	for (i=0;i<dsbuf.str_len;++i) {
254 	    tmp[i] = dsbuf.str[i];
255 	}
256 	write_outbuf(tmp, dsbuf.str_len);
257 	FREE(tmp);
258     }
259     if (dsbuf.str)
260       FREE((void *) dsbuf.str);
261     return res;
262 }
263 
264 void
ConInit(void)265 ConInit(void)
266 {
267     unsigned tid;
268 
269     console_input = CreateSemaphore(NULL, 0, 1, NULL);
270     console_output = CreateSemaphore(NULL, 0, 1, NULL);
271     console_input_event = CreateManualEvent(FALSE);
272     console_thread = (HANDLE *) _beginthreadex(NULL, 0,
273 					       ConThreadInit,
274 					       0, 0, &tid);
275 
276     /* Make all erts_*printf on stdout and stderr use con_vprintf */
277     erts_printf_stdout_func = con_vprintf;
278     erts_printf_stderr_func = con_vprintf;
279 }
280 
281 /*
282   ConNormalExit() is called from erts_exit() when the emulator
283   is stopping. If the exit has not been initiated by this
284   console thread (WM_DESTROY or ID_BREAK), the function must
285   invoke the console thread to save the user preferences.
286 */
287 void
ConNormalExit(void)288 ConNormalExit(void)
289 {
290   if (!destroyed)
291     SendMessage(hFrameWnd, WM_SAVE_PREFS, 0L, 0L);
292 }
293 
294 void
ConWaitForExit(void)295 ConWaitForExit(void)
296 {
297     ConPrintf("\n\nAbnormal termination\n");
298     WaitForSingleObject(console_thread, INFINITE);
299 }
300 
ConSetCtrlHandler(BOOL (WINAPI * handler)(DWORD))301 void ConSetCtrlHandler(BOOL (WINAPI *handler)(DWORD))
302 {
303     ctrl_handler = handler;
304 }
305 
ConPutChar(Uint32 c)306 int ConPutChar(Uint32 c)
307 {
308     TCHAR sbuf[1];
309 #ifdef HARDDEBUG
310     fprintf(stderr,"ConPutChar: %d\n",(int) c);
311     fflush(stderr);
312 #endif
313     sbuf[0] = c;
314     write_outbuf(sbuf, 1);
315     return 1;
316 }
317 
GetXFromLine(HDC hdc,int hscroll,int xpos,ScreenLine_t * pLine)318 static int GetXFromLine(HDC hdc, int hscroll, int xpos,ScreenLine_t *pLine)
319 {
320    SIZE size;
321    int hscrollPix = hscroll * cxChar;
322 
323    if (pLine == NULL) {
324        return 0;
325    }
326 
327    if (pLine->width < xpos) {
328        return (canvasColumns-hscroll)*cxChar;
329    }
330    /* Not needed (?): SelectObject(hdc,CreateFontIndirect(&logfont)); */
331    if (GetTextExtentPoint32(hdc,pLine->text,xpos,&size)) {
332 #ifdef HARDDEBUG
333        fprintf(stderr,"size.cx:%d\n",(int)size.cx);
334        fflush(stderr);
335 #endif
336        if (hscrollPix >= size.cx) {
337 	   return 0;
338        }
339        return ((int) size.cx) - hscrollPix;
340    } else {
341        return (xpos-hscroll)*cxChar;
342    }
343 }
344 
GetXFromCurrentY(HDC hdc,int hscroll,int xpos)345 static int GetXFromCurrentY(HDC hdc, int hscroll, int xpos) {
346     return GetXFromLine(hdc, hscroll, xpos, GetLineFromY(cur_y));
347 }
348 
ConSetCursor(int from,int to)349 void ConSetCursor(int from, int to)
350 {   TCHAR cmd[9];
351     int *p;
352     //DebugBreak();
353     cmd[0] = SET_CURSOR;
354     /*
355      * XXX Expect trouble on CPUs which don't allow misaligned read and writes.
356      */
357     p = (int *)&cmd[1];
358     *p++ = from;
359     *p = to;
360     write_outbuf(cmd, 1 + (2*sizeof(int)/sizeof(TCHAR)));
361 }
362 
ConPrintf(char * format,...)363 void ConPrintf(char *format, ...)
364 {
365     va_list va;
366 
367     va_start(va, format);
368     (void) con_vprintf(format, va);
369     va_end(va);
370 }
371 
ConBeep(void)372 void ConBeep(void)
373 {
374     SendMessage(hClientWnd, WM_CONBEEP, 0L, 0L);
375 }
376 
ConReadInput(Uint32 * data,int num_chars)377 int ConReadInput(Uint32 *data, int num_chars)
378 {
379     TCHAR *buf;
380     int nread;
381     WaitForSingleObject(console_input,INFINITE);
382     nread = num_chars = min(num_chars,inbuf.wrPos-inbuf.rdPos);
383     buf = &inbuf.data[inbuf.rdPos];
384     inbuf.rdPos += nread;
385     while (nread--)
386         *data++ = *buf++;
387     if (inbuf.rdPos >= inbuf.wrPos) {
388         inbuf.rdPos = 0;
389         inbuf.wrPos = 0;
390         ResetEvent(console_input_event);
391     }
392     ReleaseSemaphore(console_input,1,NULL);
393     return num_chars;
394 }
395 
ConGetKey(void)396 int ConGetKey(void)
397 {
398     Uint32 c;
399     WaitForSingleObject(console_input,INFINITE);
400     ResetEvent(console_input_event);
401     inbuf.rdPos = inbuf.wrPos = 0;
402     ReleaseSemaphore(console_input,1,NULL);
403     WaitForSingleObject(console_input_event,INFINITE);
404     ConReadInput(&c, 1);
405     return (int) c;
406 }
407 
ConGetColumns(void)408 int ConGetColumns(void)
409 {
410     return (int) canvasColumns; /* 32bit atomic on windows */
411 }
412 
ConGetRows(void)413 int ConGetRows(void) {
414     return (int) canvasRows;
415 }
416 
417 
418 static HINSTANCE hInstance;
419 extern HMODULE beam_module;
420 
421 static unsigned __stdcall
ConThreadInit(LPVOID param)422 ConThreadInit(LPVOID param)
423 {
424     MSG msg;
425     WNDCLASSEX wndclass;
426     int iCmdShow;
427     STARTUPINFO StartupInfo;
428     HACCEL hAccel;
429     int x, y, w, h;
430     struct title_buf title;
431 
432     /*DebugBreak();*/
433     hInstance = GetModuleHandle(NULL);
434     StartupInfo.dwFlags = 0;
435     GetStartupInfo(&StartupInfo);
436     iCmdShow = StartupInfo.dwFlags & STARTF_USESHOWWINDOW ?
437 	StartupInfo.wShowWindow : SW_SHOWDEFAULT;
438 
439     LoadUserPreferences();
440 
441     /* frame window class */
442     wndclass.cbSize	        = sizeof (wndclass);
443     wndclass.style          = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
444     wndclass.lpfnWndProc    = FrameWndProc;
445     wndclass.cbClsExtra     = 0;
446     wndclass.cbWndExtra     = 0;
447     wndclass.hInstance      = hInstance;
448     wndclass.hIcon          = LoadIcon (hInstance, MAKEINTRESOURCE(1));
449     wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW);
450     wndclass.hbrBackground  = NULL;
451     wndclass.lpszMenuName   = NULL;
452     wndclass.lpszClassName  = szFrameClass;
453     wndclass.hIconSm	    = LoadIcon (hInstance, MAKEINTRESOURCE(1));
454     RegisterClassExW (&wndclass);
455 
456     /* client window class */
457     wndclass.cbSize	        = sizeof (wndclass);
458     wndclass.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
459     wndclass.lpfnWndProc    = ClientWndProc;
460     wndclass.cbClsExtra     = 0;
461     wndclass.cbWndExtra     = 0;
462     wndclass.hInstance      = hInstance;
463     wndclass.hIcon          = LoadIcon (hInstance, MAKEINTRESOURCE(1));
464     wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW);
465     wndclass.hbrBackground  = CreateSolidBrush(bkgColor);
466     wndclass.lpszMenuName   = NULL;
467     wndclass.lpszClassName  = szClientClass;
468     wndclass.hIconSm	    = LoadIcon (hInstance, MAKEINTRESOURCE(1));
469     RegisterClassExW (&wndclass);
470 
471     InitCommonControls();
472     init_buffers();
473 
474     nBufLines = 0;
475     buffer_top = cur_line = ConNewLine();
476     cur_line->next = buffer_bottom = ConNewLine();
477     buffer_bottom->prev = cur_line;
478 
479     /* Create Frame Window */
480     window_title(&title);
481     hFrameWnd = CreateWindowEx(0, szFrameClass, title.name,
482 			       WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,
483 			       CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
484 			       NULL,LoadMenu(beam_module,MAKEINTRESOURCE(1)),
485 			       hInstance,NULL);
486     free_window_title(&title);
487 
488     /* XXX OTP-5522:
489        The window position is not saved correctly and if the window
490        is closed when minimized, it's not possible to start werl again
491        with the window open. Temporary fix so far is to ignore saved values
492        and always start with initial settings. */
493     /* Original:   if (winPos.left == -1) {  */
494     /* Temporary:  if (1) { */
495     if (1) {
496 
497 	/* initial window position */
498 	x = 0;
499 	y = 0;
500 	w = cxChar*LINE_LENGTH+FRAME_WIDTH+GetSystemMetrics(SM_CXVSCROLL);
501 	h = cyChar*30+FRAME_HEIGHT;
502     } else {
503 	/* saved window position */
504 	x = winPos.left;
505 	y = winPos.top;
506 	w = winPos.right - x;
507 	h = winPos.bottom - y;
508     }
509     SetWindowPos(hFrameWnd, NULL, x, y, w, h, SWP_NOZORDER);
510 
511     ShowWindow(hFrameWnd, iCmdShow);
512     UpdateWindow(hFrameWnd);
513 
514     hAccel = LoadAccelerators(beam_module,MAKEINTRESOURCE(1));
515 
516     ReleaseSemaphore(console_input, 1, NULL);
517     ReleaseSemaphore(console_output, 1, NULL);
518 
519 
520     /* Main message loop */
521     while (GetMessage (&msg, NULL, 0, 0))
522     {
523         if (!TranslateAccelerator(hFrameWnd,hAccel,&msg))
524         {
525             TranslateMessage (&msg);
526             DispatchMessage (&msg);
527         }
528     }
529     /*
530        PostQuitMessage() results in WM_QUIT which makes GetMessage()
531        return 0 (which stops the main loop). Before we return from
532        the console thread, the ctrl_handler is called to do erts_exit.
533     */
534     (*ctrl_handler)(CTRL_CLOSE_EVENT);
535     return msg.wParam;
536 }
537 
538 static LRESULT CALLBACK
FrameWndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)539 FrameWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
540 {
541     RECT r;
542     int cy,i,bufsize;
543     TCHAR c;
544     unsigned long l;
545     TCHAR buf[128];
546     struct title_buf title;
547 
548     switch (iMsg) {
549     case WM_CREATE:
550         /* client window creation */
551 	window_title(&title);
552         hClientWnd = CreateWindowEx(WS_EX_CLIENTEDGE, szClientClass, title.name,
553 				    WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL,
554 				    CW_USEDEFAULT, CW_USEDEFAULT,
555 				    CW_USEDEFAULT, CW_USEDEFAULT,
556 				    hwnd, (HMENU)0, hInstance, NULL);
557 	free_window_title(&title);
558         hTBWnd = InitToolBar(hwnd);
559         UpdateWindow (hClientWnd);
560         return 0;
561     case WM_SIZE :
562         if (IsWindowVisible(hTBWnd)) {
563             SendMessage(hTBWnd,TB_AUTOSIZE,0,0L);
564             GetWindowRect(hTBWnd,&r);
565             cy = r.bottom-r.top;
566         } else cy = 0;
567         MoveWindow(hClientWnd,0,cy,LOWORD(lParam),HIWORD(lParam)-cy,TRUE);
568         return 0;
569     case WM_ERASEBKGND:
570         return 1;
571     case WM_SETFOCUS :
572         CreateCaret(hClientWnd, NULL, cxChar, cyChar);
573         SetCaretPos(GetXFromCurrentY(GetDC(hClientWnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
574         ShowCaret(hClientWnd);
575         return 0;
576     case WM_KILLFOCUS:
577         HideCaret(hClientWnd);
578         DestroyCaret();
579         return 0;
580     case WM_INITMENUPOPUP :
581         if (lParam == 0)	/* File popup menu */
582         {
583             EnableMenuItem((HMENU)wParam, IDMENU_STARTLOG,
584 			   logfile ? MF_GRAYED : MF_ENABLED);
585             EnableMenuItem((HMENU)wParam, IDMENU_STOPLOG,
586 			   logfile ? MF_ENABLED : MF_GRAYED);
587             return 0;
588 	}
589         else if (lParam == 1)	/* Edit popup menu */
590         {
591             EnableMenuItem((HMENU)wParam, IDMENU_COPY,
592 			   fTextSelected ? MF_ENABLED : MF_GRAYED);
593             EnableMenuItem((HMENU)wParam, IDMENU_PASTE,
594 			   IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
595             return 0;
596         }
597         else if (lParam == 3)	/* View popup menu */
598         {
599             CheckMenuItem((HMENU)wParam,IDMENU_TOOLBAR,
600 			  IsWindowVisible(hTBWnd) ? MF_CHECKED : MF_UNCHECKED);
601             return 0;
602         }
603         break;
604     case WM_NOTIFY:
605         switch (((LPNMHDR) lParam)->code) {
606         case TTN_NEEDTEXT:
607             {
608 		LPTOOLTIPTEXT lpttt;
609 		lpttt = (LPTOOLTIPTEXT) lParam;
610 		lpttt->hinst = hInstance;
611 		/* check for combobox handle */
612 		if (lpttt->uFlags&TTF_IDISHWND) {
613 		    if ((lpttt->hdr.idFrom == (UINT) hComboWnd)) {
614 			lstrcpy(lpttt->lpszText,TEXT("Command History"));
615 			break;
616 		    }
617 		}
618 		/* check for toolbar buttons */
619 		switch (lpttt->hdr.idFrom) {
620                 case IDMENU_COPY:
621                     lstrcpy(lpttt->lpszText,TEXT("Copy (Ctrl+C)"));
622                     break;
623                 case IDMENU_PASTE:
624                     lstrcpy(lpttt->lpszText,TEXT("Paste (Ctrl+V)"));
625                     break;
626 		case IDMENU_FONT:
627                     lstrcpy(lpttt->lpszText,TEXT("Fonts"));
628                     break;
629 		case IDMENU_ABOUT:
630                     lstrcpy(lpttt->lpszText,TEXT("Help"));
631                     break;
632 		}
633 	    }
634         }
635         break;
636     case WM_COMMAND:
637 	switch(LOWORD(wParam))
638 	{
639 	case IDMENU_STARTLOG:
640             OpenLogFile(hwnd);
641 	    return 0;
642 	case IDMENU_STOPLOG:
643             CloseLogFile(hwnd);
644 	    return 0;
645 	case IDMENU_EXIT:
646 	    SendMessage(hwnd, WM_CLOSE, 0, 0L);
647 	    return 0;
648 	case IDMENU_COPY:
649 	    if (fTextSelected)
650                 OnEditCopy(hClientWnd);
651 	    return 0;
652 	case IDMENU_PASTE:
653 	    OnEditPaste(hClientWnd);
654 	    return 0;
655         case IDMENU_SELALL:
656             OnEditSelAll(hClientWnd);
657             return 0;
658 	case IDMENU_FONT:
659 	    if (ConChooseFont(hClientWnd)) {
660 		ConSetFont(hClientWnd);
661 	    }
662 	    SaveUserPreferences();
663 	    return 0;
664         case IDMENU_SELECTBKG:
665             ConChooseColor(hClientWnd);
666 	    SaveUserPreferences();
667             return 0;
668         case IDMENU_TOOLBAR:
669             if (toolbarVisible) {
670                 ShowWindow(hTBWnd,SW_HIDE);
671 		toolbarVisible = FALSE;
672             } else {
673                 ShowWindow(hTBWnd,SW_SHOW);
674 		toolbarVisible = TRUE;
675 	    }
676             GetClientRect(hwnd,&r);
677             PostMessage(hwnd,WM_SIZE,0,MAKELPARAM(r.right,r.bottom));
678             return 0;
679         case IDMENU_ABOUT:
680             DialogBox(beam_module,TEXT("AboutBox"),hwnd,AboutDlgProc);
681             return 0;
682         case ID_COMBOBOX:
683             switch (HIWORD(wParam)) {
684             case CBN_SELENDOK:
685                 i = SendMessage(hComboWnd,CB_GETCURSEL,0,0);
686                 if (i != CB_ERR) {
687                     buf[0] = 0x01; /* CTRL+A */
688                     buf[1] = 0x0B; /* CTRL+K */
689                     bufsize = SendMessage(hComboWnd,CB_GETLBTEXT,i,(LPARAM)&buf[2]);
690                     if (bufsize != CB_ERR)
691                         write_inbuf(buf,bufsize+2);
692                     SetFocus(hwnd);
693                 }
694                 break;
695             case CBN_SELENDCANCEL:
696                 break;
697             }
698 	    break;
699 	case ID_BREAK:		  /* CTRL+BRK */
700 	    /* pass on break char if the ctrl_handler is disabled */
701 	    if ((*ctrl_handler)(CTRL_C_EVENT) == FALSE) {
702 		c = 0x03;
703 		write_inbuf(&c,1);
704 	    }
705 	    return 0;
706         }
707         break;
708     case WM_KEYDOWN :
709 	switch (wParam) {
710 	case VK_UP: c = 'P'-'@'; break;
711 	case VK_DOWN : c = 'N'-'@'; break;
712 	case VK_RIGHT : c = 'F'-'@'; break;
713 	case VK_LEFT : c = 'B'-'@'; break;
714 	case VK_DELETE : c = 'D' -'@'; break;
715 	case VK_HOME : c = 'A'-'@'; break;
716 	case VK_END : c = 'E'-'@'; break;
717         case VK_RETURN : AddToCmdHistory(); return 0;
718 	case VK_PRIOR :   /* PageUp */
719 	    PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEUP, 0);
720 	    return 0;
721 	case VK_NEXT :   /* PageDown */
722 	    PostMessage(hClientWnd, WM_VSCROLL, SB_PAGEDOWN, 0);
723 	    return 0;
724 	default: return 0;
725 	}
726         write_inbuf(&c, 1);
727 	return 0;
728     case WM_MOUSEWHEEL:
729       {
730 	int delta = GET_WHEEL_DELTA_WPARAM(wParam);
731 	if (delta < 0) {
732 	  PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK,
733 						       (iVscrollPos + 5)),0);
734 	} else {
735 	  WORD pos = ((iVscrollPos - 5) < 0) ? 0 : (iVscrollPos - 5);
736 	  PostMessage(hClientWnd, WM_VSCROLL, MAKELONG(SB_THUMBTRACK,pos),0);
737 	}
738       return 0;
739       }
740     case WM_CHAR:
741 	c = (TCHAR)wParam;
742         write_inbuf(&c,1);
743 	return 0;
744     case WM_CLOSE :
745 	break;
746     case WM_DESTROY :
747 	SaveUserPreferences();
748 	destroyed = TRUE;
749 	PostQuitMessage(0);
750 	return 0;
751     case WM_SAVE_PREFS :
752 	SaveUserPreferences();
753 	return 0;
754     }
755     return DefWindowProc(hwnd, iMsg, wParam, lParam);
756 }
757 
758 static BOOL
Client_OnCreate(HWND hwnd,LPCREATESTRUCT lpCreateStruct)759 Client_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
760 {
761     ConFontInitialize(hwnd);
762     cur_x = cur_y = 0;
763     iVscrollPos = 0;
764     iHscrollPos = 0;
765     return TRUE;
766 }
767 
768 static void
Client_OnPaint(HWND hwnd)769 Client_OnPaint(HWND hwnd)
770 {
771     ScreenLine_t *pLine;
772     int x,y,i,iTop,iBot;
773     PAINTSTRUCT ps;
774     RECT rcInvalid;
775     HDC hdc;
776 
777     hdc = BeginPaint(hwnd, &ps);
778     rcInvalid = ps.rcPaint;
779     hdc = ps.hdc;
780     iTop = max(0, iVscrollPos + rcInvalid.top/cyChar);
781     iBot = min(nBufLines, iVscrollPos + rcInvalid.bottom/cyChar+1);
782     pLine = GetLineFromY(iTop);
783     for (i = iTop; i < iBot && pLine != NULL; i++) {
784       	y = cyChar*(i-iVscrollPos);
785         x = -cxChar*iHscrollPos;
786 	TextOut(hdc, x, y, &pLine->text[0], pLine->width);
787         pLine = pLine->next;
788     }
789     if (fTextSelected || fSelecting) {
790 	InvertSelectionArea(hwnd);
791     }
792     SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
793     EndPaint(hwnd, &ps);
794 }
795 #ifdef HARDDEBUG
dump_linebufs(void)796 static void dump_linebufs(void) {
797     char *buff;
798     ScreenLine_t *s = buffer_top;
799     fprintf(stderr,"LinebufDump------------------------\n");
800     while(s) {
801 	if (s == buffer_top) fprintf(stderr,"BT-> ");
802 	if (s == buffer_bottom) fprintf(stderr,"BB-> ");
803 	if (s == cur_line) fprintf(stderr,"CL-> ");
804 
805 	buff = (char *) ALLOC(s->width+1);
806 	memcpy(buff,s->text,s->width);
807 	buff[s->width] = '\0';
808 	fprintf(stderr,"{\"%s\",%d,%d}\n",buff,s->newline,s->allocated);
809 	FREE(buff);
810 	s = s->next;
811     }
812     fprintf(stderr,"LinebufDumpEnd---------------------\n");
813     fflush(stderr);
814 }
815 #endif
816 
reorganize_linebufs(HWND hwnd)817 static void reorganize_linebufs(HWND hwnd) {
818     ScreenLine_t *otop = buffer_top;
819     ScreenLine_t *obot = buffer_bottom;
820     ScreenLine_t *next;
821     int i,cpos;
822 
823     cpos = 0;
824     i = nBufLines - cur_y;
825     while (i > 1) {
826 	cpos += obot->width;
827 	obot = obot->prev;
828 	i--;
829     }
830     cpos += (obot->width - cur_x);
831 #ifdef HARDDEBUG
832     fprintf(stderr,"nBufLines = %d, cur_x = %d, cur_y = %d, cpos = %d\n",
833 	    nBufLines,cur_x,cur_y,cpos);
834     fflush(stderr);
835 #endif
836 
837 
838     nBufLines = 0;
839     buffer_top = cur_line = ConNewLine();
840     cur_line->next = buffer_bottom = ConNewLine();
841     buffer_bottom->prev = cur_line;
842 
843     cur_x = cur_y = 0;
844     iVscrollPos = 0;
845     iHscrollPos = 0;
846 
847     while(otop) {
848 	for(i=0;i<otop->width;++i) {
849 	    cur_line->text[cur_x] = otop->text[i];
850 	    cur_x++;
851             if (cur_x > cur_line->width)
852 		cur_line->width = cur_x;
853 	    if (GetXFromCurrentY(GetDC(hwnd),0,cur_x) + cxChar >
854 		(LINE_LENGTH * cxChar)) {
855                 ConCarriageFeed(0);
856 	    }
857 	}
858 	if (otop->newline) {
859 	    ConCarriageFeed(1);
860             /*ConScrollScreen();*/
861 	}
862 	next = otop->next;
863 	FREE(otop->text);
864 	FREE(otop);
865 	otop = next;
866     }
867     while (cpos) {
868 	cur_x--;
869 	if (cur_x < 0) {
870 	    cur_y--;
871 	    cur_line = cur_line->prev;
872 	    cur_x = cur_line->width-1;
873 	}
874 	cpos--;
875     }
876     SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
877 #ifdef HARDDEBUG
878     fprintf(stderr,"canvasColumns = %d,nBufLines = %d, cur_x = %d, cur_y = %d\n",
879 	    canvasColumns,nBufLines,cur_x,cur_y);
880     fflush(stderr);
881 #endif
882 }
883 
884 
885 static void
Client_OnSize(HWND hwnd,UINT state,int cx,int cy)886 Client_OnSize(HWND hwnd, UINT state, int cx, int cy)
887 {
888     RECT r;
889     SCROLLBARINFO sbi;
890     int w,h,columns;
891     int scrollheight;
892     cxClient = cx;
893     cyClient = cy;
894     set_scroll_info(hwnd);
895     GetClientRect(hwnd,&r);
896     w = r.right - r.left;
897     h = r.bottom - r.top;
898     sbi.cbSize = sizeof(SCROLLBARINFO);
899     if (!GetScrollBarInfo(hwnd, OBJID_HSCROLL,&sbi) ||
900 	(sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)) {
901 	scrollheight = 0;
902     } else {
903 	scrollheight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
904     }
905     canvasRows = (h - scrollheight) / cyChar;
906     if (canvasRows < DEF_CANVAS_ROWS) {
907 	canvasRows = DEF_CANVAS_ROWS;
908     }
909     columns = (w - GetSystemMetrics(SM_CXVSCROLL)) /cxChar;
910     if (columns < DEF_CANVAS_COLUMNS)
911 	columns = DEF_CANVAS_COLUMNS;
912     if (columns != canvasColumns) {
913 	canvasColumns = columns;
914 	/*dump_linebufs();*/
915 	reorganize_linebufs(hwnd);
916 	fSelecting = fTextSelected = FALSE;
917 	InvalidateRect(hwnd, NULL, TRUE);
918 #ifdef HARDDEBUG
919 	fprintf(stderr,"Paint: cols = %d, rows = %d\n",canvasColumns,canvasRows);
920 	fflush(stderr);
921 #endif
922     }
923 
924     SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
925 }
926 
calc_charpoint_from_point(HDC dc,int x,int y,int y_offset,POINT * pt)927 static void calc_charpoint_from_point(HDC dc, int x, int y, int y_offset, POINT *pt)
928 {
929     int r;
930     int hscrollPix = iHscrollPos * cxChar;
931 
932     pt->y = y/cyChar + iVscrollPos + y_offset;
933 
934     if (x > (LINE_LENGTH-iHscrollPos) * cxChar) {
935 	x =  (LINE_LENGTH-iHscrollPos) * cxChar;
936     }
937     if (pt->y - y_offset > 0 && GetLineFromY(pt->y - y_offset) == NULL) {
938 	pt->y = nBufLines - 1 + y_offset;
939 	pt->x = GetLineFromY(pt->y - y_offset)->width;
940     } else {
941 	for (pt->x = 1;
942 	     (r = GetXFromLine(dc, 0, pt->x, GetLineFromY(pt->y - y_offset))) != 0 &&
943 		 (r - hscrollPix) < x;
944 	     ++(pt->x))
945 	    ;
946 	if ((r - hscrollPix) > x)
947 	    --(pt->x);
948 #ifdef HARD_SEL_DEBUG
949 	fprintf(stderr,"pt->x = %d, iHscrollPos = %d\n",(int) pt->x, iHscrollPos);
950 	fflush(stderr);
951 #endif
952 	if (pt->x <= 0) {
953 	    pt->x = x/cxChar + iHscrollPos;
954 	}
955     }
956 }
957 
958 
959 static void
Client_OnLButtonDown(HWND hwnd,BOOL fDoubleClick,int x,int y,UINT keyFlags)960 Client_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
961 {
962     int r;
963     SetFocus(GetParent(hwnd));	/* In case combobox steals the focus */
964 #ifdef HARD_SEL_DEBUG
965     fprintf(stderr,"OnLButtonDown fSelecting = %d, fTextSelected = %d:\n",
966 	    fSelecting,fTextSelected);
967     fflush(stderr);
968 #endif
969     if (fTextSelected) {
970 	InvertSelectionArea(hwnd);
971     }
972     fTextSelected = FALSE;
973 
974     calc_charpoint_from_point(GetDC(hwnd), x, y, 0, &editBeg);
975 
976     editEnd.x = editBeg.x;
977     editEnd.y = editBeg.y + 1;
978     fSelecting = TRUE;
979     SetCapture(hwnd);
980 }
981 
982 static void
Client_OnRButtonDown(HWND hwnd,BOOL fDoubleClick,int x,int y,UINT keyFlags)983 Client_OnRButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
984 {
985     if (fTextSelected) {
986 	fSelecting = TRUE;
987 	Client_OnMouseMove(hwnd,x,y,keyFlags);
988 	fSelecting = FALSE;
989     }
990 }
991 
992 static void
Client_OnLButtonUp(HWND hwnd,int x,int y,UINT keyFlags)993 Client_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
994 {
995 #ifdef HARD_SEL_DEBUG
996     fprintf(stderr,"OnLButtonUp fSelecting = %d, fTextSelected = %d:\n",
997 	    fSelecting,fTextSelected);
998     fprintf(stderr,"(Beg.x = %d, Beg.y = %d, "
999 	    "End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
1000 	    editEnd.x,editEnd.y);
1001 #endif
1002     if (fSelecting &&
1003 	!(editBeg.x == editEnd.x && editBeg.y == (editEnd.y - 1))) {
1004 	fTextSelected = TRUE;
1005     }
1006 #ifdef HARD_SEL_DEBUG
1007     fprintf(stderr,"OnLButtonUp fTextSelected = %d:\n",
1008 	    fTextSelected);
1009     fflush(stderr);
1010 #endif
1011     fSelecting = FALSE;
1012     ReleaseCapture();
1013 }
1014 
1015 #define EMPTY_RECT(R) \
1016 (((R).bottom - (R).top == 0) || ((R).right - (R).left == 0))
1017 #define ABS(X) (((X)< 0) ? -1 * (X) : X)
1018 #define DIFF(A,B) ABS(((int)(A)) - ((int)(B)))
1019 
diff_sel_area(RECT old[3],RECT new[3],RECT result[6])1020 static int diff_sel_area(RECT old[3], RECT new[3], RECT result[6])
1021 {
1022     int absposold = old[0].left + old[0].top * canvasColumns;
1023     int absposnew = new[0].left + new[0].top * canvasColumns;
1024     int absendold = absposold, absendnew = absposnew;
1025     int i, x, ret = 0;
1026     int abspos[2],absend[2];
1027     for(i = 0; i < 3; ++i) {
1028 	if (!EMPTY_RECT(old[i])) {
1029 	    absendold += (old[i].right - old[i].left) *
1030 		(old[i].bottom - old[i].top);
1031 	}
1032 	if (!EMPTY_RECT(new[i])) {
1033 	    absendnew += (new[i].right - new[i].left) *
1034 		(new[i].bottom - new[i].top);
1035 	}
1036     }
1037     abspos[0] = min(absposold, absposnew);
1038     absend[0] = DIFF(absposold, absposnew) + abspos[0];
1039     abspos[1] = min(absendold, absendnew);
1040     absend[1] = DIFF(absendold, absendnew) + abspos[1];
1041 #ifdef HARD_SEL_DEBUG
1042     fprintf(stderr,"abspos[0] = %d, absend[0] = %d, abspos[1] = %d, absend[1] = %d\n",abspos[0],absend[0],abspos[1],absend[1]);
1043     fflush(stderr);
1044 #endif
1045     i = 0;
1046     for (x = 0; x < 2; ++x) {
1047 	if (abspos[x] != absend[x]) {
1048 	    int consumed = 0;
1049 	    result[i].left = abspos[x] % canvasColumns;
1050 	    result[i].top = abspos[x] / canvasColumns;
1051 	    result[i].bottom = result[i].top + 1;
1052 	    if ((absend[x] - abspos[x]) + result[i].left < canvasColumns) {
1053 #ifdef HARD_SEL_DEBUG
1054 		fprintf(stderr,"Nowrap, %d < canvasColumns\n",
1055 			(absend[x] - abspos[x]) + result[i].left);
1056 		fflush(stderr);
1057 #endif
1058 		result[i].right = (absend[x] - abspos[x]) + result[i].left;
1059 		consumed += result[i].right - result[i].left;
1060 	    } else {
1061 #ifdef HARD_SEL_DEBUG
1062 		fprintf(stderr,"Wrap, %d >= canvasColumns\n",
1063 			(absend[x] - abspos[x]) + result[i].left);
1064 		fflush(stderr);
1065 #endif
1066 		result[i].right = canvasColumns;
1067 		consumed += result[i].right - result[i].left;
1068 		if (absend[x] - abspos[x] - consumed >= canvasColumns) {
1069 		    ++i;
1070 		    result[i].top = result[i-1].bottom;
1071 		    result[i].left = 0;
1072 		    result[i].right = canvasColumns;
1073 		    result[i].bottom = (absend[x] - abspos[x] - consumed) / canvasColumns + result[i].top;
1074 		    consumed += (result[i].bottom - result[i].top) * canvasColumns;
1075 		}
1076 		if (absend[x] - abspos[x] - consumed > 0) {
1077 		    ++i;
1078 		    result[i].top = result[i-1].bottom;
1079 		    result[i].bottom = result[i].top + 1;
1080 		    result[i].left = 0;
1081 		    result[i].right = absend[x] - abspos[x] - consumed;
1082 		}
1083 	    }
1084 	    ++i;
1085 	}
1086     }
1087 #ifdef HARD_SEL_DEBUG
1088     if (i > 2) {
1089 	int x;
1090 	fprintf(stderr,"i = %d\n",i);
1091 	fflush(stderr);
1092 	for (x = 0; x < i; ++x) {
1093 	    fprintf(stderr, "result[%d]: top = %d, left = %d, "
1094 		    "bottom = %d. right = %d\n",
1095 		    x, result[x].top, result[x].left,
1096 		    result[x].bottom, result[x].right);
1097 	}
1098     }
1099 #endif
1100     return i;
1101 }
1102 
1103 
1104 
calc_sel_area(RECT rects[3],POINT beg,POINT end)1105 static void calc_sel_area(RECT rects[3], POINT beg, POINT end)
1106 {
1107     /* These are not really rects and points, these are character
1108        based positions, need to be multiplied by cxChar and cyChar to
1109        make up canvas coordinates */
1110     memset(rects,0,3*sizeof(RECT));
1111     rects[0].left = beg.x;
1112     rects[0].top = beg.y;
1113     rects[0].bottom = beg.y+1;
1114     if (end.y - beg.y == 1) { /* Only one row */
1115 	rects[0].right = end.x;
1116 	goto out;
1117     }
1118     rects[0].right = canvasColumns;
1119     if (end.y - beg.y > 2) {
1120 	rects[1].left = 0;
1121 	rects[1].top = rects[0].bottom;
1122 	rects[1].right = canvasColumns;
1123 	rects[1].bottom = end.y - 1;
1124     }
1125     rects[2].left = 0;
1126     rects[2].top = end.y - 1;
1127     rects[2].bottom = end.y;
1128     rects[2].right = end.x;
1129 
1130  out:
1131 #ifdef HARD_SEL_DEBUG
1132     {
1133 	int i;
1134 	fprintf(stderr,"beg.x = %d, beg.y = %d, end.x = %d, end.y = %d\n",
1135 		beg.x,beg.y,end.x,end.y);
1136 	for (i = 0; i < 3; ++i) {
1137 	    fprintf(stderr,"[%d] left = %d, top = %d, "
1138 		    "right = %d, bottom = %d\n",
1139 		    i, rects[i].left, rects[i].top,
1140 		    rects[i].right, rects[i].bottom);
1141 	}
1142 	fflush(stderr);
1143     }
1144 #endif
1145     return;
1146 }
1147 
calc_sel_area_turned(RECT rects[3],POINT eBeg,POINT eEnd)1148 static void calc_sel_area_turned(RECT rects[3], POINT eBeg, POINT eEnd) {
1149     POINT from,to;
1150     if (eBeg.y >=  eEnd.y ||
1151 	(eBeg.y == eEnd.y - 1 && eBeg.x > eEnd.x)) {
1152 #ifdef HARD_SEL_DEBUG
1153 	fprintf(stderr,"Reverting (Beg.x = %d, Beg.y = %d, "
1154 		"End.x = %d, End.y = %d)\n",eBeg.x,eBeg.y,
1155 		eEnd.x,eEnd.y);
1156 	fflush(stderr);
1157 #endif
1158 	from.x = eEnd.x;
1159 	from.y = eEnd.y - 1;
1160 	to.x = eBeg.x;
1161 	to.y = eBeg.y + 1;
1162 	calc_sel_area(rects,from,to);
1163     } else {
1164 	calc_sel_area(rects,eBeg,eEnd);
1165     }
1166 }
1167 
1168 
InvertSelectionArea(HWND hwnd)1169 static void InvertSelectionArea(HWND hwnd)
1170 {
1171     RECT rects[3];
1172     POINT from,to;
1173     int i;
1174     calc_sel_area_turned(rects,editBeg,editEnd);
1175     for (i = 0; i < 3; ++i) {
1176 	if (!EMPTY_RECT(rects[i])) {
1177 	    from.x = rects[i].left;
1178 	    to.x = rects[i].right;
1179 	    from.y = rects[i].top;
1180 	    to.y = rects[i].bottom;
1181 	    DrawSelection(hwnd,from,to);
1182 	}
1183     }
1184 }
1185 
1186 static void
Client_OnMouseMove(HWND hwnd,int x,int y,UINT keyFlags)1187 Client_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
1188 {
1189     if (fSelecting) {
1190 	RECT rold[3], rnew[3], rupdate[6];
1191 	int num_updates,i,r;
1192 	POINT from,to;
1193 	calc_sel_area_turned(rold,editBeg,editEnd);
1194 
1195 	calc_charpoint_from_point(GetDC(hwnd), x, y, 1, &editEnd);
1196 
1197 	calc_sel_area_turned(rnew,editBeg,editEnd);
1198 	num_updates = diff_sel_area(rold,rnew,rupdate);
1199 	for (i = 0; i < num_updates;++i) {
1200 	    from.x = rupdate[i].left;
1201 	    to.x = rupdate[i].right;
1202 	    from.y = rupdate[i].top;
1203 	    to.y = rupdate[i].bottom;
1204 #ifdef HARD_SEL_DEBUG
1205 	    fprintf(stderr,"from: x=%d,y=%d, to: x=%d, y=%d\n",
1206 		    from.x, from.y,to.x,to.y);
1207 	    fflush(stderr);
1208 #endif
1209 	    DrawSelection(hwnd,from,to);
1210 	}
1211     }
1212 }
1213 
1214 static void
Client_OnVScroll(HWND hwnd,HWND hwndCtl,UINT code,int pos)1215 Client_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
1216 {
1217     int iVscroll;
1218 
1219     switch(code) {
1220     case SB_LINEDOWN:
1221 	iVscroll = 1;
1222 	break;
1223     case SB_LINEUP:
1224 	iVscroll = -1;
1225 	break;
1226     case SB_PAGEDOWN:
1227 	iVscroll = max(1, cyClient/cyChar);
1228 	break;
1229     case SB_PAGEUP:
1230 	iVscroll = min(-1, -cyClient/cyChar);
1231 	break;
1232     case SB_THUMBTRACK:
1233 	iVscroll = pos - iVscrollPos;
1234 	break;
1235     default:
1236 	iVscroll = 0;
1237     }
1238     iVscroll = max(-iVscrollPos, min(iVscroll, iVscrollMax-iVscrollPos));
1239     if (iVscroll != 0) {
1240 	iVscrollPos += iVscroll;
1241 	ScrollWindowEx(hwnd, 0, -cyChar*iVscroll, NULL, NULL,
1242 		       NULL, NULL, SW_ERASE | SW_INVALIDATE);
1243 	SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
1244 	iVscroll = GetScrollPos(hwnd, SB_VERT);
1245 	UpdateWindow(hwnd);
1246     }
1247 }
1248 
1249 static void
Client_OnHScroll(HWND hwnd,HWND hwndCtl,UINT code,int pos)1250 Client_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
1251 {
1252     int iHscroll, curCharWidth = cxClient/cxChar;
1253 
1254     switch(code) {
1255     case SB_LINEDOWN:
1256 	iHscroll = 1;
1257 	break;
1258     case SB_LINEUP:
1259 	iHscroll = -1;
1260 	break;
1261     case SB_PAGEDOWN:
1262 	iHscroll = max(1,curCharWidth-1);
1263 	break;
1264     case SB_PAGEUP:
1265 	iHscroll = min(-1,-(curCharWidth-1));
1266 	break;
1267     case SB_THUMBTRACK:
1268 	iHscroll = pos - iHscrollPos;
1269 	break;
1270     default:
1271 	iHscroll = 0;
1272     }
1273     iHscroll = max(-iHscrollPos, min(iHscroll, iHscrollMax-iHscrollPos-(curCharWidth-1)));
1274     if (iHscroll != 0) {
1275 	iHscrollPos += iHscroll;
1276 	ScrollWindow(hwnd, -cxChar*iHscroll, 0, NULL, NULL);
1277 	SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);
1278 	UpdateWindow(hwnd);
1279     }
1280 }
1281 
1282 static LRESULT CALLBACK
ClientWndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)1283 ClientWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
1284 {
1285     switch (iMsg) {
1286 	HANDLE_MSG(hwnd, WM_CREATE, Client_OnCreate);
1287 	HANDLE_MSG(hwnd, WM_SIZE, Client_OnSize);
1288 	HANDLE_MSG(hwnd, WM_PAINT, Client_OnPaint);
1289 	HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Client_OnLButtonDown);
1290 	HANDLE_MSG(hwnd, WM_RBUTTONDOWN, Client_OnRButtonDown);
1291 	HANDLE_MSG(hwnd, WM_LBUTTONUP, Client_OnLButtonUp);
1292 	HANDLE_MSG(hwnd, WM_MOUSEMOVE, Client_OnMouseMove);
1293 	HANDLE_MSG(hwnd, WM_VSCROLL, Client_OnVScroll);
1294 	HANDLE_MSG(hwnd, WM_HSCROLL, Client_OnHScroll);
1295     case WM_CONBEEP:
1296         if (0) Beep(440, 400);
1297         return 0;
1298     case WM_CONTEXT:
1299         ConDrawText(hwnd);
1300         return 0;
1301     case WM_CLOSE:
1302         break;
1303     case WM_DESTROY:
1304         PostQuitMessage(0);
1305         return 0;
1306     }
1307     return DefWindowProc (hwnd, iMsg, wParam, lParam);
1308 }
1309 
1310 static void
LoadUserPreferences(void)1311 LoadUserPreferences(void)
1312 {
1313     DWORD size;
1314     DWORD res;
1315     DWORD type;
1316 
1317     /* default prefs */
1318     GetObject(GetStockObject(SYSTEM_FIXED_FONT),sizeof(LOGFONT),(PSTR)&logfont);
1319     fgColor = GetSysColor(COLOR_WINDOWTEXT);
1320     bkgColor = GetSysColor(COLOR_WINDOW);
1321     winPos.left = -1;
1322     toolbarVisible = TRUE;
1323 
1324     if (RegCreateKeyEx(HKEY_CURRENT_USER, USER_KEY, 0, 0,
1325 		       REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
1326 		       &key, &res) != ERROR_SUCCESS)
1327         return;
1328     has_key = TRUE;
1329     if (res == REG_CREATED_NEW_KEY)
1330 	return;
1331     size = sizeof(logfont);
1332     res = RegQueryValueEx(key,TEXT("Font"),NULL,&type,(LPBYTE)&logfont,&size);
1333     size = sizeof(fgColor);
1334     res = RegQueryValueEx(key,TEXT("FgColor"),NULL,&type,(LPBYTE)&fgColor,&size);
1335     size = sizeof(bkgColor);
1336     res = RegQueryValueEx(key,TEXT("BkColor"),NULL,&type,(LPBYTE)&bkgColor,&size);
1337     size = sizeof(winPos);
1338     res = RegQueryValueEx(key,TEXT("Pos"),NULL,&type,(LPBYTE)&winPos,&size);
1339     size = sizeof(toolbarVisible);
1340     res = RegQueryValueEx(key,TEXT("Toolbar"),NULL,&type,(LPBYTE)&toolbarVisible,&size);
1341 }
1342 
1343 static void
SaveUserPreferences(void)1344 SaveUserPreferences(void)
1345 {
1346     WINDOWPLACEMENT wndPlace;
1347 
1348     if (has_key == TRUE) {
1349         RegSetValueEx(key,TEXT("Font"),0,REG_BINARY,(CONST BYTE *)&logfont,sizeof(LOGFONT));
1350         RegSetValueEx(key,TEXT("FgColor"),0,REG_DWORD,(CONST BYTE *)&fgColor,sizeof(fgColor));
1351         RegSetValueEx(key,TEXT("BkColor"),0,REG_DWORD,(CONST BYTE *)&bkgColor,sizeof(bkgColor));
1352         RegSetValueEx(key,TEXT("Toolbar"),0,REG_DWORD,(CONST BYTE *)&toolbarVisible,sizeof(toolbarVisible));
1353 
1354 	wndPlace.length = sizeof(WINDOWPLACEMENT);
1355 	GetWindowPlacement(hFrameWnd,&wndPlace);
1356 	/* If wndPlace.showCmd == SW_MINIMIZE, then the window is minimized.
1357 	   We don't care, wndPlace.rcNormalPosition always holds the last known position. */
1358 	winPos = wndPlace.rcNormalPosition;
1359 	RegSetValueEx(key,TEXT("Pos"),0,REG_BINARY,(CONST BYTE *)&winPos,sizeof(winPos));
1360     }
1361 }
1362 
1363 
1364 static void
set_scroll_info(HWND hwnd)1365 set_scroll_info(HWND hwnd)
1366 {
1367     SCROLLINFO info;
1368     int hScrollBy;
1369     /*
1370      * Set vertical scrolling range and scroll box position.
1371      */
1372 
1373     iVscrollMax = nBufLines-1;
1374     iVscrollPos = min(iVscrollPos, iVscrollMax);
1375     info.cbSize = sizeof(info);
1376     info.fMask = SIF_PAGE|SIF_RANGE|SIF_POS;
1377     info.nMin = 0;
1378     info.nPos = iVscrollPos;
1379     info.nPage = min(cyClient/cyChar, iVscrollMax);
1380     info.nMax = iVscrollMax;
1381     SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
1382 
1383     /*
1384      * Set horizontal scrolling range and scroll box position.
1385      */
1386 
1387     iHscrollMax = LINE_LENGTH-1;
1388     hScrollBy = max(0, (iHscrollPos - (iHscrollMax-cxClient/cxChar))*cxChar);
1389     iHscrollPos = min(iHscrollPos, iHscrollMax);
1390     info.nPos = iHscrollPos;
1391     info.nPage = cxClient/cxChar;
1392     info.nMax = iHscrollMax;
1393     SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1394     /*ScrollWindow(hwnd, hScrollBy, 0, NULL, NULL);*/
1395 }
1396 
1397 
1398 static void
ensure_line_below(void)1399 ensure_line_below(void)
1400 {
1401     if (cur_line->next == NULL) {
1402 	if (nBufLines >= lines_to_save) {
1403 	    ScreenLine_t* pLine = buffer_top->next;
1404 	    FREE(buffer_top->text);
1405 	    FREE(buffer_top);
1406 	    buffer_top = pLine;
1407 	    buffer_top->prev = NULL;
1408 	    nBufLines--;
1409 	}
1410 	cur_line->next = ConNewLine();
1411 	cur_line->next->prev = cur_line;
1412 	buffer_bottom = cur_line->next;
1413 	set_scroll_info(hClientWnd);
1414     }
1415 }
1416 
1417 static ScreenLine_t*
ConNewLine(void)1418 ConNewLine(void)
1419 {
1420     ScreenLine_t *pLine;
1421 
1422     pLine = (ScreenLine_t *)ALLOC(sizeof(ScreenLine_t));
1423     if (!pLine)
1424 	return NULL;
1425     pLine->text = (TCHAR *) ALLOC(canvasColumns * sizeof(TCHAR));
1426 #ifdef HARDDEBUG
1427     pLine->allocated = canvasColumns;
1428 #endif
1429     pLine->width = 0;
1430     pLine->prev = pLine->next = NULL;
1431     pLine->newline = 0;
1432     nBufLines++;
1433     return pLine;
1434 }
1435 
1436 static ScreenLine_t*
GetLineFromY(int y)1437 GetLineFromY(int y)
1438 {
1439     ScreenLine_t *pLine = buffer_top;
1440     int i;
1441 
1442     for (i = 0; i < nBufLines && pLine != NULL; i++) {
1443         if (i == y)
1444 	    return pLine;
1445         pLine  = pLine->next;
1446     }
1447     return NULL;
1448 }
1449 
ConCarriageFeed(int hard_newline)1450 void ConCarriageFeed(int hard_newline)
1451 {
1452     cur_x = 0;
1453     ensure_line_below();
1454     cur_line->newline = hard_newline;
1455     cur_line = cur_line->next;
1456     if (cur_y < nBufLines-1) {
1457 	cur_y++;
1458     } else if (iVscrollPos > 0) {
1459 	iVscrollPos--;
1460     }
1461 }
1462 
1463 /*
1464  * Scroll screen if cursor is not visible.
1465  */
1466 static void
ConScrollScreen(void)1467 ConScrollScreen(void)
1468 {
1469     if (cur_y >= iVscrollPos + cyClient/cyChar) {
1470 	int iVscroll;
1471 
1472 	iVscroll = cur_y - iVscrollPos - cyClient/cyChar + 1;
1473 	iVscrollPos += iVscroll;
1474 	ScrollWindowEx(hClientWnd, 0, -cyChar*iVscroll, NULL, NULL,
1475 		     NULL, NULL, SW_ERASE | SW_INVALIDATE);
1476 	SetScrollPos(hClientWnd, SB_VERT, iVscrollPos, TRUE);
1477 	UpdateWindow(hClientWnd);
1478     }
1479 }
1480 
1481 static void
DrawSelection(HWND hwnd,POINT pt1,POINT pt2)1482 DrawSelection(HWND hwnd, POINT pt1, POINT pt2)
1483 {
1484     HDC hdc;
1485     int width,height;
1486 #ifdef HARD_SEL_DEBUG
1487     fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
1488 	    (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
1489 #endif
1490     pt1.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt1.x,GetLineFromY(pt1.y));
1491     pt2.x = GetXFromLine(GetDC(hwnd),iHscrollPos,pt2.x,GetLineFromY(pt2.y-1));
1492     pt1.y -= iVscrollPos;
1493     pt2.y -= iVscrollPos;
1494     pt1.y *= cyChar;
1495     pt2.y *= cyChar;
1496 #ifdef HARD_SEL_DEBUG
1497     fprintf(stderr,"pt1.x = %d, pt1.y = %d, pt2.x = %d, pt2.y = %d\n",
1498 	    (int) pt1.x, (int) pt1.y, (int) pt2.x, (int) pt2.y);
1499     fflush(stderr);
1500 #endif
1501     width = pt2.x-pt1.x;
1502     height = pt2.y - pt1.y;
1503     hdc = GetDC(hwnd);
1504     PatBlt(hdc,pt1.x,pt1.y,width,height,DSTINVERT);
1505     ReleaseDC(hwnd,hdc);
1506 }
1507 
1508 static void
OnEditCopy(HWND hwnd)1509 OnEditCopy(HWND hwnd)
1510 {
1511     HGLOBAL hMem;
1512     TCHAR *pMem;
1513     ScreenLine_t *pLine;
1514     RECT rects[3];
1515     POINT from,to;
1516     int i,j,sum,len;
1517     if (editBeg.y >=  editEnd.y ||
1518 	(editBeg.y == editEnd.y - 1 && editBeg.x > editEnd.x)) {
1519 #ifdef HARD_SEL_DEBUG
1520 	fprintf(stderr,"CopyReverting (Beg.x = %d, Beg.y = %d, "
1521 		"End.x = %d, End.y = %d)\n",editBeg.x,editBeg.y,
1522 		editEnd.x,editEnd.y);
1523 	fflush(stderr);
1524 #endif
1525 	from.x = editEnd.x;
1526 	from.y = editEnd.y - 1;
1527 	to.x = editBeg.x;
1528 	to.y = editBeg.y + 1;
1529 	calc_sel_area(rects,from,to);
1530     } else {
1531 	calc_sel_area(rects,editBeg,editEnd);
1532     }
1533     sum = 1;
1534     for (i = 0; i < 3; ++i) {
1535 	if (!EMPTY_RECT(rects[i])) {
1536 	    pLine = GetLineFromY(rects[i].top);
1537 	    for (j = rects[i].top; j < rects[i].bottom ;++j) {
1538 		if (pLine == NULL) {
1539 		    sum += 2;
1540 		    break;
1541 		}
1542 		if (pLine->width > rects[i].left) {
1543 		    sum += (pLine->width < rects[i].right) ?
1544 			pLine->width -  rects[i].left :
1545 			rects[i].right - rects[i].left;
1546 		}
1547 		if(pLine->newline && rects[i].right >= pLine->width) {
1548 		    sum += 2;
1549 		}
1550 		pLine = pLine->next;
1551 	    }
1552 	}
1553     }
1554 #ifdef HARD_SEL_DEBUG
1555     fprintf(stderr,"sum = %d\n",sum);
1556     fflush(stderr);
1557 #endif
1558     hMem = GlobalAlloc(GHND, sum * sizeof(TCHAR));
1559     pMem = GlobalLock(hMem);
1560     for (i = 0; i < 3; ++i) {
1561 	if (!EMPTY_RECT(rects[i])) {
1562 	    pLine = GetLineFromY(rects[i].top);
1563 	    for (j = rects[i].top; j < rects[i].bottom; ++j) {
1564 		if (pLine == NULL) {
1565 		    memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
1566 		    pMem += 2;
1567 		    break;
1568 		}
1569 		if (pLine->width > rects[i].left) {
1570 		    len = (pLine->width < rects[i].right) ?
1571 			pLine->width -  rects[i].left :
1572 			rects[i].right - rects[i].left;
1573 		    memcpy(pMem,pLine->text + rects[i].left,len * sizeof(TCHAR));
1574 		    pMem +=len;
1575 		}
1576 		if(pLine->newline && rects[i].right >= pLine->width) {
1577 		    memcpy(pMem,TEXT("\r\n"),2 * sizeof(TCHAR));
1578 		    pMem += 2;
1579 		}
1580 		pLine = pLine->next;
1581 	    }
1582 	}
1583     }
1584     *pMem = TEXT('\0');
1585     /* Flash de selection area to give user feedback about copying */
1586     InvertSelectionArea(hwnd);
1587     Sleep(100);
1588     InvertSelectionArea(hwnd);
1589 
1590     OpenClipboard(hwnd);
1591     EmptyClipboard();
1592     GlobalUnlock(hMem);
1593     SetClipboardData(CF_UNICODETEXT,hMem);
1594     CloseClipboard();
1595 }
1596 
1597 /* XXX:PaN Tchar or char? */
1598 static void
OnEditPaste(HWND hwnd)1599 OnEditPaste(HWND hwnd)
1600 {
1601     HANDLE hClipMem;
1602     TCHAR *pClipMem,*pMem,*pMem2;
1603     if (!OpenClipboard(hwnd))
1604 	return;
1605     if ((hClipMem = GetClipboardData(CF_UNICODETEXT)) != NULL) {
1606         pClipMem = GlobalLock(hClipMem);
1607         pMem = (TCHAR *)ALLOC(GlobalSize(hClipMem) * sizeof(TCHAR));
1608         pMem2 = pMem;
1609         while ((*pMem2 = *pClipMem) != TEXT('\0')) {
1610             if (*pClipMem == TEXT('\r'))
1611                 *pMem2 = TEXT('\n');
1612             ++pMem2;
1613 	    ++pClipMem;
1614         }
1615         GlobalUnlock(hClipMem);
1616         write_inbuf(pMem, _tcsclen(pMem));
1617     }
1618     CloseClipboard();
1619 }
1620 
1621 static void
OnEditSelAll(HWND hwnd)1622 OnEditSelAll(HWND hwnd)
1623 {
1624     editBeg.x = 0;
1625     editBeg.y = 0;
1626     editEnd.x = LINE_LENGTH-1;
1627     editEnd.y = cur_y;
1628     fTextSelected = TRUE;
1629     InvalidateRect(hwnd, NULL, TRUE);
1630 }
1631 
CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)1632 CF_HOOK_RET APIENTRY CFHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
1633 {
1634     /* Hook procedure for font dialog box */
1635     HWND hOwner;
1636     RECT rc,rcOwner,rcDlg;
1637     switch (iMsg) {
1638     case WM_INITDIALOG:
1639         /* center dialogbox within its owner window */
1640         if ((hOwner = GetParent(hDlg)) == NULL)
1641             hOwner = GetDesktopWindow();
1642         GetWindowRect(hOwner, &rcOwner);
1643         GetWindowRect(hDlg, &rcDlg);
1644         CopyRect(&rc, &rcOwner);
1645         OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1646         OffsetRect(&rc, -rc.left, -rc.top);
1647         OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1648         SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
1649 		     rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
1650         return (CF_HOOK_RET) 1;
1651     default:
1652         break;
1653     }
1654     return (CF_HOOK_RET) 0; /* Let the default procedure process the message */
1655 }
1656 
1657 static BOOL
ConChooseFont(HWND hwnd)1658 ConChooseFont(HWND hwnd)
1659 {
1660     HDC hdc;
1661     hdc = GetDC(hwnd);
1662     cf.lStructSize = sizeof(CHOOSEFONT);
1663     cf.hwndOwner = hwnd;
1664     cf.hDC = NULL;
1665     cf.lpLogFont = &logfont;
1666     cf.iPointSize = 0;
1667     cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_SCREENFONTS|CF_FIXEDPITCHONLY|CF_EFFECTS|CF_ENABLEHOOK;
1668     cf.rgbColors = GetTextColor(hdc);
1669     cf.lCustData = 0L;
1670     cf.lpfnHook = CFHookProc;
1671     cf.lpTemplateName = NULL;
1672     cf.hInstance = NULL;
1673     cf.lpszStyle = NULL;
1674     cf.nFontType = 0;
1675     cf.nSizeMin = 0;
1676     cf.nSizeMax = 0;
1677     ReleaseDC(hwnd,hdc);
1678     return ChooseFont(&cf);
1679 }
1680 
1681 static void
ConFontInitialize(HWND hwnd)1682 ConFontInitialize(HWND hwnd)
1683 {
1684     HDC hdc;
1685     TEXTMETRIC tm;
1686     HFONT hFont;
1687 
1688     hFont = CreateFontIndirect(&logfont);
1689     hdc = GetDC(hwnd);
1690     SelectObject(hdc, hFont);
1691     SetTextColor(hdc,fgColor);
1692     SetBkColor(hdc,bkgColor);
1693     GetTextMetrics(hdc, &tm);
1694     cxChar = tm.tmAveCharWidth;
1695     cxCharMax = tm.tmMaxCharWidth;
1696     cyChar = tm.tmHeight + tm.tmExternalLeading;
1697     ReleaseDC(hwnd, hdc);
1698 }
1699 
1700 static void
ConSetFont(HWND hwnd)1701 ConSetFont(HWND hwnd)
1702 {
1703     HDC hdc;
1704     TEXTMETRIC tm;
1705     HFONT hFontNew;
1706 
1707     hFontNew = CreateFontIndirect(&logfont);
1708     SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
1709 		MAKELPARAM(1,0));
1710     hdc = GetDC(hwnd);
1711     DeleteObject(SelectObject(hdc, hFontNew));
1712     GetTextMetrics(hdc, &tm);
1713     cxChar = tm.tmAveCharWidth;
1714     cxCharMax = tm.tmMaxCharWidth;
1715     cyChar = tm.tmHeight + tm.tmExternalLeading;
1716     fgColor = cf.rgbColors;
1717     SetTextColor(hdc,fgColor);
1718     ReleaseDC(hwnd, hdc);
1719     set_scroll_info(hwnd);
1720     HideCaret(hwnd);
1721     if (DestroyCaret()) {
1722         CreateCaret(hwnd, NULL, cxChar, cyChar);
1723         SetCaretPos(GetXFromCurrentY(hdc,iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
1724     }
1725     ShowCaret(hwnd);
1726     InvalidateRect(hwnd, NULL, TRUE);
1727 }
1728 
1729 CC_HOOK_RET APIENTRY
CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)1730 CCHookProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)
1731 {
1732     /* Hook procedure for choose color dialog box */
1733     HWND hOwner;
1734     RECT rc,rcOwner,rcDlg;
1735     switch (iMsg) {
1736     case WM_INITDIALOG:
1737         /* center dialogbox within its owner window */
1738         if ((hOwner = GetParent(hDlg)) == NULL)
1739             hOwner = GetDesktopWindow();
1740         GetWindowRect(hOwner, &rcOwner);
1741         GetWindowRect(hDlg, &rcDlg);
1742         CopyRect(&rc, &rcOwner);
1743         OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1744         OffsetRect(&rc, -rc.left, -rc.top);
1745         OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1746         SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
1747 		     rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
1748         return (CC_HOOK_RET) 1;
1749     default:
1750         break;
1751     }
1752     return (CC_HOOK_RET) 0;			/* Let the default procedure process the message */
1753 }
1754 
ConChooseColor(HWND hwnd)1755 void ConChooseColor(HWND hwnd)
1756 {
1757     CHOOSECOLOR cc;
1758     static COLORREF acrCustClr[16];
1759     HBRUSH hbrush;
1760     HDC hdc;
1761 
1762     /* Initialize CHOOSECOLOR */
1763     ZeroMemory(&cc, sizeof(CHOOSECOLOR));
1764     cc.lStructSize = sizeof(CHOOSECOLOR);
1765     cc.hwndOwner = hwnd;
1766     cc.lpCustColors = (LPDWORD) acrCustClr;
1767     cc.rgbResult = bkgColor;
1768     cc.lpfnHook = CCHookProc;
1769     cc.Flags = CC_FULLOPEN|CC_RGBINIT|CC_SOLIDCOLOR|CC_ENABLEHOOK;
1770 
1771     if (ChooseColor(&cc)==TRUE) {
1772         bkgColor = cc.rgbResult;
1773         hdc = GetDC(hwnd);
1774         SetBkColor(hdc,bkgColor);
1775         ReleaseDC(hwnd,hdc);
1776         hbrush = CreateSolidBrush(bkgColor);
1777         DeleteObject((HBRUSH)SetClassLong(hClientWnd,GCL_HBRBACKGROUND,(LONG)hbrush));
1778         InvalidateRect(hwnd,NULL,TRUE);
1779     }
1780 }
1781 
OFNHookProc(HWND hwndDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)1782 OFN_HOOK_RET APIENTRY OFNHookProc(HWND hwndDlg,UINT iMsg,
1783 				  WPARAM wParam,LPARAM lParam)
1784 {
1785     /* Hook procedure for open file dialog box */
1786     HWND hOwner,hDlg;
1787     RECT rc,rcOwner,rcDlg;
1788     hDlg = GetParent(hwndDlg);
1789     switch (iMsg) {
1790     case WM_INITDIALOG:
1791         /* center dialogbox within its owner window */
1792         if ((hOwner = GetParent(hDlg)) == NULL)
1793             hOwner = GetDesktopWindow();
1794         GetWindowRect(hOwner, &rcOwner);
1795         GetWindowRect(hDlg, &rcDlg);
1796         CopyRect(&rc, &rcOwner);
1797         OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1798         OffsetRect(&rc, -rc.left, -rc.top);
1799         OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1800         SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
1801 		     rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
1802         return (OFN_HOOK_RET) 1;
1803     default:
1804         break;
1805     }
1806     return (OFN_HOOK_RET) 0; /* the let default procedure process the message */
1807 }
1808 
1809 static void
GetFileName(HWND hwnd,TCHAR * pFile)1810 GetFileName(HWND hwnd, TCHAR *pFile)
1811 {
1812     /* Open the File Open dialog box and */
1813     /* retrieve the file name            */
1814     OPENFILENAME ofn;
1815     TCHAR szFilterSpec [128] = TEXT("logfiles (*.log)\0*.log\0All files (*.*)\0*.*\0\0");
1816     #define MAXFILENAME 256
1817     TCHAR szFileName[MAXFILENAME];
1818     TCHAR szFileTitle[MAXFILENAME];
1819 
1820     /* these need to be filled in */
1821     _tcscpy(szFileName, TEXT("erlshell.log"));
1822     _tcscpy(szFileTitle, TEXT("")); /* must be NULL */
1823 
1824     ofn.lStructSize       = sizeof(OPENFILENAME);
1825     ofn.hwndOwner         = NULL;
1826     ofn.lpstrFilter       = szFilterSpec;
1827     ofn.lpstrCustomFilter = NULL;
1828     ofn.nMaxCustFilter    = 0;
1829     ofn.nFilterIndex      = 0;
1830     ofn.lpstrFile         = szFileName;
1831     ofn.nMaxFile          = MAXFILENAME;
1832     ofn.lpstrInitialDir   = NULL;
1833     ofn.lpstrFileTitle    = szFileTitle;
1834     ofn.nMaxFileTitle     = MAXFILENAME;
1835     ofn.lpstrTitle        = TEXT("Open logfile");
1836     ofn.lpstrDefExt       = TEXT("log");
1837     ofn.Flags             = OFN_CREATEPROMPT|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_ENABLEHOOK|OFN_NOCHANGEDIR; /* OFN_NOCHANGEDIR only works in Vista :( */
1838     ofn.lpfnHook          = OFNHookProc;
1839 
1840     if (!GetOpenFileName ((LPOPENFILENAME)&ofn)){
1841         *pFile = TEXT('\0');
1842     } else {
1843         _tcscpy(pFile, ofn.lpstrFile);
1844     }
1845 }
1846 
OpenLogFile(HWND hwnd)1847 void OpenLogFile(HWND hwnd)
1848 {
1849     /* open a file for logging */
1850     TCHAR filename[_MAX_PATH];
1851 
1852     GetFileName(hwnd, filename);
1853     if (filename[0] == '\0')
1854         return;
1855     if (NULL == (logfile = _tfopen(filename,TEXT("w,ccs=UNICODE"))))
1856         return;
1857 }
1858 
CloseLogFile(HWND hwnd)1859 void CloseLogFile(HWND hwnd)
1860 {
1861     /* close log file */
1862     fclose(logfile);
1863     logfile = NULL;
1864 }
1865 
LogFileWrite(TCHAR * buf,int num_chars)1866 void LogFileWrite(TCHAR *buf, int num_chars)
1867 {
1868     /* write to logfile */
1869     int from,to;
1870     while (num_chars-- > 0) {
1871         switch (*buf) {
1872         case SET_CURSOR:
1873             buf++;
1874             from = *((int *)buf);
1875             buf += sizeof(int)/sizeof(TCHAR);
1876             to = *((int *)buf);
1877             buf += (sizeof(int)/sizeof(TCHAR))-1;
1878             num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
1879 	    // Wont seek in Unicode file, sorry...
1880             // fseek(logfile,to-from *sizeof(TCHAR),SEEK_CUR);
1881             break;
1882         default:
1883             _fputtc(*buf,logfile);
1884             break;
1885         }
1886         buf++;
1887     }
1888 }
1889 
1890 static void
init_buffers(void)1891 init_buffers(void)
1892 {
1893     inbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
1894     outbuf.data = (TCHAR *) ALLOC(BUFSIZE * sizeof(TCHAR));
1895     inbuf.size = BUFSIZE;
1896     inbuf.rdPos = inbuf.wrPos = 0;
1897     outbuf.size = BUFSIZE;
1898     outbuf.rdPos = outbuf.wrPos = 0;
1899 }
1900 
1901 static int
check_realloc(buffer_t * buf,int num_chars)1902 check_realloc(buffer_t *buf, int num_chars)
1903 {
1904     if (buf->wrPos + num_chars >= buf->size) {
1905 	if (buf->size > MAXBUFSIZE)
1906 	    return 0;
1907 	buf->size += num_chars + BUFSIZE;
1908 	if (!(buf->data = (TCHAR *)REALLOC(buf->data, buf->size * sizeof(TCHAR)))) {
1909 	    buf->size = buf->rdPos = buf->wrPos = 0;
1910 	    return 0;
1911         }
1912     }
1913     return 1;
1914 }
1915 
1916 static int
write_inbuf(TCHAR * data,int num_chars)1917 write_inbuf(TCHAR *data, int num_chars)
1918 {
1919     TCHAR *buf;
1920     int nwrite;
1921     WaitForSingleObject(console_input,INFINITE);
1922     if (!check_realloc(&inbuf,num_chars)) {
1923         ReleaseSemaphore(console_input,1,NULL);
1924         return -1;
1925     }
1926     buf = &inbuf.data[inbuf.wrPos];
1927     inbuf.wrPos += num_chars;
1928     nwrite = num_chars;
1929     while (nwrite--)
1930         *buf++ = *data++;
1931     SetEvent(console_input_event);
1932     ReleaseSemaphore(console_input,1,NULL);
1933     return num_chars;
1934 }
1935 
1936 static int
write_outbuf(TCHAR * data,int num_chars)1937 write_outbuf(TCHAR *data, int num_chars)
1938 {
1939     TCHAR *buf;
1940     int nwrite;
1941 
1942     WaitForSingleObject(console_output,INFINITE);
1943     if (!check_realloc(&outbuf, num_chars)) {
1944         ReleaseSemaphore(console_output,1,NULL);
1945         return -1;
1946     }
1947     if (outbuf.rdPos == outbuf.wrPos)
1948         PostMessage(hClientWnd, WM_CONTEXT, 0L, 0L);
1949     buf = &outbuf.data[outbuf.wrPos];
1950     outbuf.wrPos += num_chars;
1951     nwrite = num_chars;
1952     while (nwrite--)
1953         *buf++ = *data++;
1954     ReleaseSemaphore(console_output,1,NULL);
1955     return num_chars;
1956 }
1957 
AboutDlgProc(HWND hDlg,UINT iMsg,WPARAM wParam,LPARAM lParam)1958 DIALOG_PROC_RET CALLBACK AboutDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1959 {
1960     HWND hOwner;
1961     RECT rc,rcOwner,rcDlg;
1962 
1963     switch (iMsg) {
1964     case WM_INITDIALOG:
1965         /* center dialogbox within its owner window */
1966         if ((hOwner = GetParent(hDlg)) == NULL)
1967             hOwner = GetDesktopWindow();
1968         GetWindowRect(hOwner, &rcOwner);
1969         GetWindowRect(hDlg, &rcDlg);
1970         CopyRect(&rc, &rcOwner);
1971         OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1972         OffsetRect(&rc, -rc.left, -rc.top);
1973         OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1974         SetWindowPos(hDlg,HWND_TOP,rcOwner.left + (rc.right / 2),
1975 		     rcOwner.top + (rc.bottom / 2),0,0,SWP_NOSIZE);
1976         SetDlgItemText(hDlg, ID_VERSIONSTRING,
1977 		       TEXT("Erlang emulator version ") TEXT(ERLANG_VERSION));
1978         return (DIALOG_PROC_RET) TRUE;
1979     case WM_COMMAND:
1980         switch (LOWORD(wParam)) {
1981         case IDOK:
1982         case IDCANCEL:
1983             EndDialog(hDlg,0);
1984             return (DIALOG_PROC_RET) TRUE;
1985         }
1986         break;
1987     }
1988     return (DIALOG_PROC_RET) FALSE;
1989 }
1990 
1991 static void
ConDrawText(HWND hwnd)1992 ConDrawText(HWND hwnd)
1993 {
1994     int num_chars;
1995     int nchars;
1996     TCHAR *buf;
1997     int from, to;
1998     int dl;
1999     int dc;
2000     RECT rc;
2001 
2002     WaitForSingleObject(console_output, INFINITE);
2003     nchars = 0;
2004     num_chars = outbuf.wrPos - outbuf.rdPos;
2005     buf = &outbuf.data[outbuf.rdPos];
2006     if (logfile != NULL)
2007 	LogFileWrite(buf, num_chars);
2008 
2009 
2010 #ifdef HARDDEBUG
2011     {
2012 	TCHAR *bu = (TCHAR *) ALLOC((num_chars+1) * sizeof(TCHAR));
2013 	memcpy(bu,buf,num_chars * sizeof(TCHAR));
2014 	bu[num_chars]='\0';
2015 	fprintf(stderr,TEXT("ConDrawText\"%s\"\n"),bu);
2016 	FREE(bu);
2017 	fflush(stderr);
2018     }
2019 #endif
2020     /*
2021      * Don't draw any text in the window; just update the line buffers
2022      * and invalidate the appropriate part of the window.  The window
2023      * will be updated on the next WM_PAINT message.
2024      */
2025 
2026     while (num_chars-- > 0) {
2027         switch (*buf) {
2028         case '\r':
2029             break;
2030         case '\n':
2031             if (nchars > 0) {
2032 		rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
2033 		rc.right = rc.left + cxCharMax*nchars;
2034 		rc.top = cyChar * (cur_y-iVscrollPos);
2035 		rc.bottom = rc.top + cyChar;
2036 		InvalidateRect(hwnd, &rc, TRUE);
2037                 nchars = 0;
2038 	    }
2039 	    ConCarriageFeed(1);
2040             ConScrollScreen();
2041             break;
2042         case SET_CURSOR:
2043             if (nchars > 0) {
2044 		rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
2045 		rc.right = rc.left + cxCharMax*nchars;
2046 		rc.top = cyChar * (cur_y-iVscrollPos);
2047 		rc.bottom = rc.top + cyChar;
2048 		InvalidateRect(hwnd, &rc, TRUE);
2049                 nchars = 0;
2050             }
2051             buf++;
2052             from = *((int *)buf);
2053             buf += sizeof(int)/sizeof(TCHAR);
2054             to = *((int *)buf);
2055             buf += (sizeof(int)/sizeof(TCHAR))-1;
2056             num_chars -= 2 * (sizeof(int)/sizeof(TCHAR));
2057 	    while (to > from) {
2058 		cur_x++;
2059 		if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
2060 		    (LINE_LENGTH * cxChar)) {
2061 		    cur_x = 0;
2062 		    cur_y++;
2063 		    ensure_line_below();
2064 		    cur_line = cur_line->next;
2065 		}
2066 		from++;
2067 	    }
2068 	    while (to < from) {
2069 		cur_x--;
2070 		if (cur_x < 0) {
2071 		    cur_y--;
2072 		    cur_line = cur_line->prev;
2073 		    cur_x = cur_line->width-1;
2074 		}
2075 		from--;
2076 	    }
2077 
2078             break;
2079         default:
2080             nchars++;
2081 	    cur_line->text[cur_x] = *buf;
2082 	    cur_x++;
2083             if (cur_x > cur_line->width)
2084 		cur_line->width = cur_x;
2085 	    if (GetXFromCurrentY(GetDC(hwnd),0,cur_x)+cxChar >
2086 		(LINE_LENGTH * cxChar)) {
2087                 if (nchars > 0) {
2088 		    rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
2089                     rc.right = rc.left + cxCharMax*nchars;
2090                     rc.top = cyChar * (cur_y-iVscrollPos);
2091                     rc.bottom = rc.top + cyChar;
2092 		    InvalidateRect(hwnd, &rc, TRUE);
2093                 }
2094                 ConCarriageFeed(0);
2095                 nchars = 0;
2096             }
2097         }
2098         buf++;
2099     }
2100     if (nchars > 0) {
2101 	rc.left = GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x - nchars);
2102 	rc.right = rc.left + cxCharMax*nchars;
2103 	rc.top = cyChar * (cur_y-iVscrollPos);
2104 	rc.bottom = rc.top + cyChar;
2105 	InvalidateRect(hwnd, &rc, TRUE);
2106     }
2107     ConScrollScreen();
2108     SetCaretPos(GetXFromCurrentY(GetDC(hwnd),iHscrollPos,cur_x), (cur_y-iVscrollPos)*cyChar);
2109     outbuf.wrPos = outbuf.rdPos = 0;
2110     ReleaseSemaphore(console_output, 1, NULL);
2111 }
2112 
2113 static void
AddToCmdHistory(void)2114 AddToCmdHistory(void)
2115 {
2116     int i;
2117     int size;
2118     Uint32 *buf;
2119     wchar_t cmdBuf[128];
2120 
2121     if (llen != 0) {
2122 	for (i = 0, size = 0; i < llen-1; i++) {
2123 	    /*
2124 	     * Find end of prompt.
2125 	     */
2126 	    if ((lbuf[i] == '>') && lbuf[i+1] == ' ') {
2127 		buf = &lbuf[i+2];
2128 		size = llen-i-2;
2129 		break;
2130 	    }
2131 	}
2132 	if (size > 0 && size < 128) {
2133 	    for (i = 0;i < size; ++i) {
2134 		cmdBuf[i] = (wchar_t) buf[i];
2135 	    }
2136 	    cmdBuf[size] = 0;
2137 	    SendMessage(hComboWnd,CB_INSERTSTRING,0,(LPARAM)cmdBuf);
2138 	}
2139     }
2140 }
2141 
2142 /*static TBBUTTON tbb[] =
2143 {
2144     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2145     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2146     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2147     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2148     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2149     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2150     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2151     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2152     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2153     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2154     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2155     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2156     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2157     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2158     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2159     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2160     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2161     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2162     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2163     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2164     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2165     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2166     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2167     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2168     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2169     0,          IDMENU_COPY,	TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
2170     1,	        IDMENU_PASTE,	TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
2171     2,	        IDMENU_FONT,	TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
2172     3,	        IDMENU_ABOUT,	TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, 0, 0, 0,
2173     0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP,    0, 0, 0, 0,
2174     };*/
2175 static TBBUTTON tbb[] =
2176 {
2177   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2178   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2179   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2180   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2181   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2182   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2183   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2184   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2185   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2186   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2187   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2188   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2189   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2190   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2191   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2192   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2193   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2194   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2195   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2196   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2197   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2198   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2199   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2200   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2201   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP},
2202   {0,          IDMENU_COPY,    TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
2203   {1,	       IDMENU_PASTE,   TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
2204   {2,	       IDMENU_FONT,    TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
2205   {3,	       IDMENU_ABOUT,   TBSTATE_ENABLED, TBSTYLE_AUTOSIZE},
2206   {0,          0,              TBSTATE_ENABLED, TBSTYLE_SEP}
2207 };
2208 
2209 static TBADDBITMAP tbbitmap =
2210 {
2211     HINST_COMMCTRL, IDB_STD_SMALL_COLOR,
2212 };
2213 
2214 #ifdef HARDDEBUG
2215 /* For really hard GUI startup debugging, place DEBUGBOX() macros in code
2216    and get modal message boxes with the line number. */
debug_box(int line)2217 static void debug_box(int line) {
2218   TCHAR buff[1024];
2219   swprintf(buff,1024,TEXT("DBG:%d"),line);
2220   MessageBox(NULL,buff,TEXT("DBG"),MB_OK|MB_APPLMODAL);
2221 }
2222 
2223 #define DEBUGBOX() debug_box(__LINE__)
2224 #endif
2225 
2226 static HWND
InitToolBar(HWND hwndParent)2227 InitToolBar(HWND hwndParent)
2228 {
2229     int x,y,cx;
2230     HWND hwndTB,hwndTT;
2231     RECT r;
2232     TOOLINFO ti;
2233     HFONT hFontNew;
2234     DWORD backgroundColor = GetSysColor(COLOR_BTNFACE);
2235     COLORMAP colorMap;
2236     colorMap.from = RGB(192, 192, 192);
2237     colorMap.to = backgroundColor;
2238     /* Create toolbar window with tooltips */
2239     hwndTB = CreateWindowEx(0,TOOLBARCLASSNAME,(TCHAR *)NULL,
2240 			    WS_CHILD|CCS_TOP|WS_CLIPSIBLINGS|TBSTYLE_TOOLTIPS,
2241 			    0,0,0,0,hwndParent,
2242 			    (HMENU)2,hInstance,NULL);
2243     SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE,
2244 		(WPARAM) sizeof(TBBUTTON),0);
2245     tbbitmap.hInst = NULL;
2246     tbbitmap.nID   = (UINT) CreateMappedBitmap(beam_module, 1,0, &colorMap, 1);
2247     SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM) 4,
2248 		(LPARAM) &tbbitmap);
2249 
2250     SendMessage(hwndTB,TB_ADDBUTTONS, (WPARAM) 30,
2251 		(LPARAM) tbb);
2252     if (toolbarVisible)
2253 	ShowWindow(hwndTB, SW_SHOW);
2254 
2255     /* Create combobox window */
2256     SendMessage(hwndTB,TB_GETITEMRECT,0,(LPARAM)&r);
2257     x = r.left; y = r.top;
2258     SendMessage(hwndTB,TB_GETITEMRECT,23,(LPARAM)&r);
2259     cx = r.right - x + 1;
2260     hComboWnd = CreateWindow(TEXT("combobox"),NULL,WS_VSCROLL|WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
2261 			     x,y,cx,100,hwndParent,(HMENU)ID_COMBOBOX, hInstance,NULL);
2262     SetParent(hComboWnd,hwndTB);
2263     hFontNew = CreateFontIndirect(&logfont);
2264     SendMessage(hComboWnd,WM_SETFONT,(WPARAM)hFontNew,
2265 		MAKELPARAM(1,0));
2266 
2267     /* Add tooltip for combo box */
2268     ZeroMemory(&ti,sizeof(TOOLINFO));
2269     ti.cbSize = sizeof(TOOLINFO);
2270     ti.uFlags = TTF_IDISHWND|TTF_CENTERTIP|TTF_SUBCLASS;
2271     ti.hwnd = hwndTB;;
2272     ti.uId = (UINT)hComboWnd;
2273     ti.lpszText = LPSTR_TEXTCALLBACK;
2274     hwndTT = (HWND)SendMessage(hwndTB,TB_GETTOOLTIPS,0,0);
2275     SendMessage(hwndTT,TTM_ADDTOOL,0,(LPARAM)&ti);
2276 
2277     return hwndTB;
2278 }
2279 
2280 static void
window_title(struct title_buf * tbuf)2281 window_title(struct title_buf *tbuf)
2282 {
2283     int res, i;
2284     size_t bufsz = TITLE_BUF_SZ;
2285     unsigned char charbuff[TITLE_BUF_SZ];
2286 
2287     res = erl_drv_getenv("ERL_WINDOW_TITLE", charbuff, &bufsz);
2288     if (res < 0)
2289 	tbuf->name = erlang_window_title;
2290     else if (res == 0) {
2291 	for (i = 0; i < bufsz; ++i) {
2292 	    tbuf->buf[i] = charbuff[i];
2293 	}
2294         tbuf->buf[bufsz - 1] = 0;
2295 	tbuf->name = &tbuf->buf[0];
2296     } else {
2297 	char *buf = ALLOC(bufsz);
2298 	if (!buf)
2299 	    tbuf->name = erlang_window_title;
2300 	else {
2301 	    while (1) {
2302 		char *newbuf;
2303 		res = erl_drv_getenv("ERL_WINDOW_TITLE", buf, &bufsz);
2304 		if (res <= 0) {
2305 		    if (res == 0) {
2306 			TCHAR *wbuf = ALLOC(bufsz *sizeof(TCHAR));
2307 			for (i = 0; i < bufsz ; ++i) {
2308 			    wbuf[i] = buf[i];
2309 			}
2310 			wbuf[bufsz - 1] = 0;
2311 			FREE(buf);
2312 			tbuf->name = wbuf;
2313 		    } else {
2314 			tbuf->name = erlang_window_title;
2315 			FREE(buf);
2316 		    }
2317 		    break;
2318 		}
2319 		newbuf = REALLOC(buf, bufsz);
2320 		if (newbuf)
2321 		    buf = newbuf;
2322 		else {
2323 		    tbuf->name = erlang_window_title;
2324 		    FREE(buf);
2325 		    break;
2326 		}
2327 	    }
2328 	}
2329     }
2330 }
2331 
2332 static void
free_window_title(struct title_buf * tbuf)2333 free_window_title(struct title_buf *tbuf)
2334 {
2335     if (tbuf->name != erlang_window_title && tbuf->name != &tbuf->buf[0])
2336 	FREE(tbuf->name);
2337 }
2338