OSDN Git Service

40c93a8ca3d5c2f879fefa5b79427ee910d2eccb
[ckw/ckw.git] / main.cpp
1 /*-----------------------------------------------------------------------------
2  * File: main.cpp
3  *-----------------------------------------------------------------------------
4  * Copyright (c) 2005       Kazuo Ishii <k-ishii@wb4.so-net.ne.jp>
5  *                              - original version
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *---------------------------------------------------------------------------*/
21 #include "ckw.h"
22 #include "ime_wrap.h"
23 #include "rsrc.h"
24
25 /*****************************************************************************/
26
27 HANDLE  gStdIn = NULL;  /* console */
28 HANDLE  gStdOut = NULL;
29 HANDLE  gStdErr = NULL;
30 HWND    gConWnd = NULL;
31
32 HANDLE  gChild = NULL;  /* child process */
33
34 LOGFONT gFontLog;       /* font IME */
35 HFONT   gFont;          /* font */
36 DWORD   gFontW;         /* char width */
37 DWORD   gFontH;         /* char height */
38
39 DWORD   gWinW;          /* window columns */
40 DWORD   gWinH;          /* window rows */
41
42 RECT    gFrame;         /* window frame size */
43 HBITMAP gBgBmp = NULL;  /* background image */
44 HBRUSH  gBgBrush = NULL;/* background brush */
45 DWORD   gBorderSize = 0;/* internal border */
46 DWORD   gLineSpace = 0; /* line space */
47 BOOL    gVScrollHide = FALSE;
48
49 BOOL    gImeOn = FALSE; /* IME-status */
50
51 /* screen buffer - copy */
52 CONSOLE_SCREEN_BUFFER_INFO* gCSI = NULL;
53 CHAR_INFO*      gScreen = NULL;
54 wchar_t*        gTitle = NULL;
55
56 /* setConsoleFont */
57 #define MAX_FONTS 128
58
59 typedef struct _CONSOLE_FONT {
60   DWORD index;
61   COORD dim;
62 } CONSOLE_FONT, *PCONSOLE_FONT;
63
64 typedef BOOL  (WINAPI *GetConsoleFontInfoT)( HANDLE,BOOL,DWORD,PCONSOLE_FONT );
65 typedef DWORD (WINAPI *GetNumberOfConsoleFontsT)( VOID );
66 typedef BOOL  (WINAPI *SetConsoleFontT)( HANDLE, DWORD );
67
68 GetConsoleFontInfoT             GetConsoleFontInfo;
69 GetNumberOfConsoleFontsT        GetNumberOfConsoleFonts;
70 SetConsoleFontT                 SetConsoleFont;
71
72 /* index color */
73 enum {
74         kColor0 = 0,
75                   kColor1,  kColor2,  kColor3,
76         kColor4,  kColor5,  kColor6,  kColor7,
77         kColor8,  kColor9,  kColor10, kColor11,
78         kColor12, kColor13, kColor14, kColor15,
79         kColorCursorFg,
80         kColorCursorBg,
81         kColorCursorImeFg,
82         kColorCursorImeBg,
83         /**/
84         kColorMax,
85 };
86 COLORREF gColorTable[ kColorMax ];
87
88
89 /*****************************************************************************/
90
91 #if 0
92 #include <stdio.h>
93 void trace(const char *msg)
94 {
95         fputs(msg, stdout);
96         fflush(stdout);
97 }
98 #else
99 #define trace(msg)
100 #endif
101
102 /*****************************************************************************/
103
104 BOOL WINAPI ReadConsoleOutput_Unicode(HANDLE con, CHAR_INFO* buffer,
105                                       COORD size, COORD pos, SMALL_RECT *sr)
106 {
107         if(!ReadConsoleOutputA(con, buffer, size, pos, sr))
108                 return(FALSE);
109
110         CHAR_INFO* s = buffer;
111         CHAR_INFO* e = buffer + (size.X * size.Y);
112         DWORD   codepage = GetConsoleOutputCP();
113         BYTE    ch[2];
114         WCHAR   wch;
115
116         while(s < e) {
117                 ch[0] = s->Char.AsciiChar;
118
119                 if(s->Attributes & COMMON_LVB_LEADING_BYTE) {
120                         if((s+1) < e && ((s+1)->Attributes & COMMON_LVB_TRAILING_BYTE)) {
121                                 ch[1] = (s+1)->Char.AsciiChar;
122                                 if(MultiByteToWideChar(codepage, 0, (LPCSTR)ch, 2, &wch, 1)) {
123                                         s->Char.UnicodeChar = wch;
124                                         s++;
125                                         s->Char.UnicodeChar = wch;
126                                         s++;
127                                         continue;
128                                 }
129                         }
130                 }
131
132                 if(MultiByteToWideChar(codepage, 0, (LPCSTR)ch, 1, &wch, 1)) {
133                         s->Char.UnicodeChar = wch;
134                 }
135                 s->Attributes &= ~(COMMON_LVB_LEADING_BYTE | COMMON_LVB_TRAILING_BYTE);
136                 s++;
137         }
138         return(TRUE);
139 }
140
141 /*****************************************************************************/
142
143 /*----------*/
144 inline void __draw_invert_char_rect(HDC hDC, RECT& rc)
145 {
146         rc.right++;
147         rc.bottom++;
148         rc.left   *= gFontW;
149         rc.right  *= gFontW;
150         rc.top    *= gFontH;
151         rc.bottom *= gFontH;
152         BitBlt(hDC, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, NULL,0,0, DSTINVERT);
153 }
154
155 /*----------*/
156 static void __draw_selection(HDC hDC)
157 {
158         SMALL_RECT sel;
159         if(!selectionGetArea(sel))
160                 return;
161
162         if(gCSI->srWindow.Top <= sel.Top && sel.Top <= gCSI->srWindow.Bottom)
163                 ;
164         else if(gCSI->srWindow.Top <= sel.Bottom && sel.Bottom <= gCSI->srWindow.Bottom)
165                 ;
166         else if(sel.Top < gCSI->srWindow.Top && gCSI->srWindow.Bottom < sel.Bottom)
167                 ;
168         else
169                 return;
170
171         RECT    rc;
172
173         if(sel.Top == sel.Bottom) {
174                 /* single line */
175                 rc.left  = sel.Left - gCSI->srWindow.Left;
176                 rc.right = sel.Right-1 - gCSI->srWindow.Left;
177                 rc.top   = \
178                 rc.bottom = sel.Top - gCSI->srWindow.Top;
179                 __draw_invert_char_rect(hDC, rc);
180                 return;
181         }
182
183         /* multi line */
184         if(gCSI->srWindow.Top <= sel.Top && sel.Top <= gCSI->srWindow.Bottom) {
185                 /* top */
186                 rc.left = sel.Left - gCSI->srWindow.Left;
187                 rc.right = gCSI->srWindow.Right - gCSI->srWindow.Left;
188                 rc.top = \
189                 rc.bottom = sel.Top - gCSI->srWindow.Top;
190                 __draw_invert_char_rect(hDC, rc);
191         }
192         if(sel.Top+1 <= sel.Bottom-1) {
193                 /* center */
194                 rc.left = 0;
195                 rc.right = gCSI->srWindow.Right - gCSI->srWindow.Left;
196
197                 if(gCSI->srWindow.Top <= sel.Top+1)
198                         rc.top = sel.Top+1 - gCSI->srWindow.Top;
199                 else
200                         rc.top = 0;
201
202                 if(gCSI->srWindow.Bottom >= sel.Bottom-1)
203                         rc.bottom = sel.Bottom-1 - gCSI->srWindow.Top;
204                 else
205                         rc.bottom = gCSI->srWindow.Bottom - gCSI->srWindow.Top;
206                 __draw_invert_char_rect(hDC, rc);
207         }
208         if(gCSI->srWindow.Top <= sel.Bottom && sel.Bottom <= gCSI->srWindow.Bottom) {
209                 /* bottom */
210                 rc.left = 0;
211                 rc.right = sel.Right-1 - gCSI->srWindow.Left;
212                 rc.top = \
213                 rc.bottom = sel.Bottom - gCSI->srWindow.Top;
214                 __draw_invert_char_rect(hDC, rc);
215         }
216 }
217
218 /*----------*/
219 static void __draw_screen(HDC hDC)
220 {
221         int     pntX, pntY;
222         int     x, y;
223         int     color_fg;
224         int     color_bg;
225         CHAR_INFO* ptr = gScreen;
226         int      work_color_fg = -1;
227         int      work_color_bg = -1;
228         wchar_t* work_text = new wchar_t[ CSI_WndCols(gCSI) ];
229         wchar_t* work_text_ptr;
230         INT*     work_width = new INT[ CSI_WndCols(gCSI) ];
231         INT*     work_width_ptr;
232         int      work_pntX;
233
234         pntY = 0;
235         for(y = gCSI->srWindow.Top ; y <= gCSI->srWindow.Bottom ; y++) {
236                 pntX = 0;
237                 work_pntX = 0;
238                 work_text_ptr = work_text;
239                 work_width_ptr = work_width;
240                 for(x = gCSI->srWindow.Left ; x <= gCSI->srWindow.Right ; x++) {
241
242                         if(ptr->Attributes & COMMON_LVB_TRAILING_BYTE) {
243                                 pntX += gFontW;
244                                 ptr++;
245                                 continue;
246                         }
247
248                         color_fg = ptr->Attributes & 0xF;
249                         color_bg = (ptr->Attributes>>4) & 0xF;
250
251                         if(color_fg != work_color_fg ||
252                            color_bg != work_color_bg) {
253                                 if(work_text_ptr > work_text) {
254                                         ExtTextOut(hDC, work_pntX, pntY, 0, NULL,
255                                                    (LPCWSTR)work_text,
256                                                    (UINT)(work_text_ptr - work_text),
257                                                    work_width);
258                                 }
259                                 work_text_ptr = work_text;
260                                 work_width_ptr = work_width;
261                                 work_pntX = pntX;
262                                 work_color_fg = color_fg;
263                                 work_color_bg = color_bg;
264                                 SetTextColor(hDC, gColorTable[work_color_fg]);
265                                 SetBkColor(  hDC, gColorTable[work_color_bg]);
266                                 SetBkMode(hDC, (work_color_bg) ? OPAQUE : TRANSPARENT);
267                         }
268
269                         if(ptr->Attributes & COMMON_LVB_LEADING_BYTE) {
270                                 *work_text_ptr++ = ptr->Char.UnicodeChar;
271                                 *work_width_ptr++ = gFontW * 2;
272                         }
273                         else {
274                                 *work_text_ptr++ = ptr->Char.UnicodeChar;
275                                 *work_width_ptr++ = gFontW;
276                         }
277                         pntX += gFontW;
278                         ptr++;
279                 }
280
281                 if(work_text_ptr > work_text) {
282                         ExtTextOut(hDC, work_pntX, pntY, 0, NULL,
283                                    (LPCWSTR)work_text,
284                                    (UINT)(work_text_ptr - work_text),
285                                    work_width);
286                 }
287
288                 pntY += gFontH;
289         }
290
291         /* draw selection */
292         __draw_selection(hDC);
293
294         /* draw cursor */
295         if(gCSI->srWindow.Top    <= gCSI->dwCursorPosition.Y &&
296            gCSI->srWindow.Bottom >= gCSI->dwCursorPosition.Y &&
297            gCSI->srWindow.Left   <= gCSI->dwCursorPosition.X &&
298            gCSI->srWindow.Right  >= gCSI->dwCursorPosition.X) {
299                 color_fg = (gImeOn) ? kColorCursorImeFg : kColorCursorFg;
300                 color_bg = (gImeOn) ? kColorCursorImeBg : kColorCursorBg;
301                 SetTextColor(hDC, gColorTable[ color_fg ]);
302                 SetBkColor(  hDC, gColorTable[ color_bg ]);
303                 SetBkMode(hDC, OPAQUE);
304                 pntX = gCSI->dwCursorPosition.X - gCSI->srWindow.Left;
305                 pntY = gCSI->dwCursorPosition.Y - gCSI->srWindow.Top;
306                 ptr = gScreen + CSI_WndCols(gCSI) * pntY + pntX;
307                 pntX *= gFontW;
308                 pntY *= gFontH;
309                 *work_width = (ptr->Attributes & COMMON_LVB_LEADING_BYTE) ? gFontW*2 : gFontW;
310                 ExtTextOut(hDC, pntX, pntY, 0, NULL,
311                            &ptr->Char.UnicodeChar, 1, work_width);
312         }
313
314         delete [] work_width;
315         delete [] work_text;
316 }
317
318 /*----------*/
319 void    onPaint(HWND hWnd)
320 {
321         PAINTSTRUCT ps;
322         HDC     hDC = BeginPaint(hWnd, &ps);
323         RECT    rc;
324         GetClientRect(hWnd, &rc);
325
326         HDC     hMemDC = CreateCompatibleDC(hDC);
327         HBITMAP hBmp = CreateCompatibleBitmap(hDC, rc.right-rc.left, rc.bottom-rc.top);
328         HGDIOBJ oldfont = SelectObject(hMemDC, gFont);
329         HGDIOBJ oldbmp  = SelectObject(hMemDC, hBmp);
330
331         FillRect(hMemDC, &rc, gBgBrush);
332
333         if(gScreen && gCSI) {
334                 SetWindowOrgEx(hMemDC, -(int)gBorderSize, -(int)gBorderSize, NULL);
335                 __draw_screen(hMemDC);
336                 SetWindowOrgEx(hMemDC, 0, 0, NULL);
337         }
338
339         BitBlt(hDC,rc.left,rc.top, rc.right-rc.left, rc.bottom-rc.top, hMemDC,0,0, SRCCOPY);
340
341         SelectObject(hMemDC, oldfont);
342         SelectObject(hMemDC, oldbmp);
343         DeleteObject(hBmp);
344         DeleteDC(hMemDC);
345
346         EndPaint(hWnd, &ps);
347 }
348
349 /*----------*/
350 static void __set_console_window_size(LONG cols, LONG rows)
351 {
352         CONSOLE_SCREEN_BUFFER_INFO csi;
353         GetConsoleScreenBufferInfo(gStdOut, &csi);
354
355         gWinW = cols;
356         gWinH = rows;
357
358         if(cols == CSI_WndCols(&csi) && rows == CSI_WndRows(&csi))
359                 return;
360
361         //SMALL_RECT tmp = { 0,0,0,0 };
362         //SetConsoleWindowInfo(gStdOut, TRUE, &tmp);
363
364         csi.dwSize.X = (SHORT)cols;
365         csi.srWindow.Left = 0;
366         csi.srWindow.Right = (SHORT)(cols -1);
367
368         if(csi.dwSize.Y < rows || csi.dwSize.Y == CSI_WndRows(&csi))
369                 csi.dwSize.Y = (SHORT)rows;
370
371         csi.srWindow.Bottom += (SHORT)(rows - CSI_WndRows(&csi));
372         if(csi.dwSize.Y <= csi.srWindow.Bottom) {
373                 csi.srWindow.Top -= csi.srWindow.Bottom - csi.dwSize.Y +1;
374                 csi.srWindow.Bottom = csi.dwSize.Y -1;
375         }
376
377         SetConsoleScreenBufferSize(gStdOut, csi.dwSize);
378         SetConsoleWindowInfo(gStdOut, TRUE, &csi.srWindow);
379 }
380
381 /*----------*/
382 void    onSizing(HWND hWnd, DWORD side, LPRECT rc)
383 {
384         trace("onSizing\n");
385         LONG fw = (gFrame.right - gFrame.left) + (gBorderSize * 2);
386         LONG fh = (gFrame.bottom - gFrame.top) + (gBorderSize * 2);
387         LONG width  = rc->right - rc->left;
388         LONG height = rc->bottom - rc->top;
389
390         width  -= fw;
391         width  -= width  % gFontW;
392         width  += fw;
393
394         height -= fh;
395         height -= height % gFontH;
396         height += fh;
397
398         if(side==WMSZ_LEFT || side==WMSZ_TOPLEFT || side==WMSZ_BOTTOMLEFT)
399                 rc->left = rc->right - width;
400         else
401                 rc->right = rc->left + width;
402
403         if(side==WMSZ_TOP || side==WMSZ_TOPLEFT || side==WMSZ_TOPRIGHT)
404                 rc->top = rc->bottom - height;
405         else
406                 rc->bottom = rc->top + height;
407 }
408
409 /*----------*/
410 void    onWindowPosChange(HWND hWnd, WINDOWPOS* wndpos)
411 {
412         trace("onWindowPosChange\n");
413         if(!(wndpos->flags & SWP_NOSIZE) && !IsIconic(hWnd)) {
414                 LONG fw = (gFrame.right - gFrame.left) + (gBorderSize * 2);
415                 LONG fh = (gFrame.bottom - gFrame.top) + (gBorderSize * 2);
416                 LONG width  = wndpos->cx;
417                 LONG height = wndpos->cy;
418                 width  = (width - fw) / gFontW;
419                 height = (height - fh) / gFontH;
420
421                 __set_console_window_size(width, height);
422
423                 wndpos->cx = width  * gFontW + fw;
424                 wndpos->cy = height * gFontH + fh;
425         }
426 }
427
428 static void __set_ime_position(HWND hWnd)
429 {
430         if(!gImeOn || !gCSI) return;
431         HIMC imc = ImmGetContext(hWnd);
432         LONG px = gCSI->dwCursorPosition.X - gCSI->srWindow.Left;
433         LONG py = gCSI->dwCursorPosition.Y - gCSI->srWindow.Top;
434         COMPOSITIONFORM cf;
435         cf.dwStyle = CFS_POINT;
436         cf.ptCurrentPos.x = px * gFontW + gBorderSize;
437         cf.ptCurrentPos.y = py * gFontH + gBorderSize;
438         ImmSetCompositionWindow(imc, &cf);
439         ImmReleaseContext(hWnd, imc);
440 }
441
442 /*----------*/
443 void    onTimer(HWND hWnd)
444 {
445         if(WaitForSingleObject(gChild, 0) != WAIT_TIMEOUT) {
446                 PostMessage(hWnd, WM_CLOSE, 0,0);
447                 return;
448         }
449
450         /* refresh handle */
451         if(gStdOut) CloseHandle(gStdOut);
452         gStdOut = CreateFile(L"CONOUT$", GENERIC_READ|GENERIC_WRITE,
453                              FILE_SHARE_READ|FILE_SHARE_WRITE,
454                              NULL, OPEN_EXISTING, 0, NULL);
455
456         /* title update */
457         static int timer_count = 0;
458         if((++timer_count & 0xF) == 1) {
459                 wchar_t *str = new wchar_t[256];
460                 GetConsoleTitle(str, 256);
461                 if(gTitle && !wcscmp(gTitle, str)) {
462                         delete [] str;
463                 }
464                 else {
465                         delete [] gTitle;
466                         gTitle = str;
467                         SetWindowText(hWnd, gTitle);
468                 }
469         }
470
471         CONSOLE_SCREEN_BUFFER_INFO* csi = new CONSOLE_SCREEN_BUFFER_INFO;
472         COORD   size;
473
474         GetConsoleScreenBufferInfo(gStdOut, csi);
475         size.X = CSI_WndCols(csi);
476         size.Y = CSI_WndRows(csi);
477
478         /* copy screen buffer */
479         DWORD      nb = size.X * size.Y;
480         CHAR_INFO* buffer = new CHAR_INFO[nb];
481         CHAR_INFO* ptr = buffer;
482         SMALL_RECT sr;
483         COORD      pos = { 0, 0 };
484
485         /* ReadConsoleOuput - maximum read size 64kByte?? */
486         size.Y = 0x8000 / sizeof(CHAR_INFO) / size.X;
487         sr.Left  = csi->srWindow.Left;
488         sr.Right = csi->srWindow.Right;
489         sr.Top   = csi->srWindow.Top;
490         do {
491                 sr.Bottom = sr.Top + size.Y -1;
492                 if(sr.Bottom > csi->srWindow.Bottom) {
493                         sr.Bottom = csi->srWindow.Bottom;
494                         size.Y = sr.Bottom - sr.Top +1;
495                 }
496                 ReadConsoleOutput_Unicode(gStdOut, ptr, size, pos, &sr);
497                 ptr += size.X * size.Y;
498                 sr.Top = sr.Bottom +1;
499         } while(sr.Top <= csi->srWindow.Bottom);
500
501         /* compare */
502         if(gScreen && gCSI &&
503            !memcmp(csi, gCSI, sizeof(CONSOLE_SCREEN_BUFFER_INFO)) &&
504            !memcmp(buffer, gScreen, sizeof(CHAR_INFO) * nb)) {
505                 /* no modified */
506                 delete [] buffer;
507                 delete csi;
508                 return;
509         }
510
511         /* swap buffer */
512         if(gScreen) delete [] gScreen;
513         if(gCSI) delete gCSI;
514         gScreen = buffer;
515         gCSI = csi;
516
517         /* redraw request */
518         InvalidateRect(hWnd, NULL, TRUE);
519
520         /* set vertical scrollbar status */
521         if(!gVScrollHide) {
522                 SCROLLINFO si;
523                 si.cbSize = sizeof(si);
524                 si.fMask = SIF_DISABLENOSCROLL | SIF_POS | SIF_PAGE | SIF_RANGE;
525                 si.nPos = gCSI->srWindow.Top;
526                 si.nPage = CSI_WndRows(gCSI);
527                 si.nMin = 0;
528                 si.nMax = gCSI->dwSize.Y-1;
529                 SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
530         }
531
532         if(gImeOn) {
533                 __set_ime_position(hWnd);
534         }
535
536         int w = CSI_WndCols(gCSI);
537         int h = CSI_WndRows(gCSI);
538         if(gWinW != w || gWinH != h) {
539                 w = (w * gFontW) + (gBorderSize * 2) + (gFrame.right - gFrame.left);
540                 h = (h * gFontH) + (gBorderSize * 2) + (gFrame.bottom - gFrame.top);
541                 SetWindowPos(hWnd, NULL, 0,0,w,h, SWP_NOMOVE|SWP_NOZORDER);
542         }
543 }
544
545 /*****************************************************************************/
546
547 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
548 {
549         switch(msg) {
550         case WM_CREATE:
551                 {
552                         HIMC imc = ImmGetContext(hWnd);
553                         ImmSetCompositionFontW(imc, &gFontLog);
554                         ImmReleaseContext(hWnd, imc);
555                 }
556                 SetTimer(hWnd, 0x3571, 10, NULL);
557                 break;
558         case WM_DESTROY:
559                 KillTimer(hWnd, 0x3571);
560                 PostQuitMessage(0);
561                 if(WaitForSingleObject(gChild, 0) == WAIT_TIMEOUT)
562                         TerminateProcess(gChild, 0);
563                 break;
564         case WM_TIMER:
565                 onTimer(hWnd);
566                 break;
567
568         case WM_ERASEBKGND:
569                 break;
570         case WM_PAINT:
571                 onPaint(hWnd);
572                 break;
573
574         case WM_SIZING:
575                 onSizing(hWnd, (DWORD)wp, (LPRECT)lp);
576                 break;
577         case WM_WINDOWPOSCHANGING:
578         case WM_WINDOWPOSCHANGED:
579                 onWindowPosChange(hWnd, (WINDOWPOS*)lp);
580                 selectionClear(hWnd);
581                 break;
582         case WM_LBUTTONDOWN:
583                 onLBtnDown(hWnd, (short)LOWORD(lp), (short)HIWORD(lp));
584                 break;
585         case WM_LBUTTONUP:
586                 onLBtnUp(hWnd, (short)LOWORD(lp), (short)HIWORD(lp));
587                 break;
588         case WM_MOUSEMOVE:
589                 onMouseMove(hWnd, (short)LOWORD(lp),(short)HIWORD(lp));
590                 break;
591         case WM_MBUTTONDOWN:
592         case WM_RBUTTONDOWN:
593                 onPasteFromClipboard(hWnd);
594                 break;
595         case WM_DROPFILES:
596                 onDropFile((HDROP)wp);
597                 break;
598
599         case WM_IME_STARTCOMPOSITION:
600                 __set_ime_position(hWnd);
601                 return( DefWindowProc(hWnd, msg, wp, lp) );
602         case WM_IME_NOTIFY:
603                 if(wp == IMN_SETOPENSTATUS) {
604                         HIMC imc = ImmGetContext(hWnd);
605                         gImeOn = ImmGetOpenStatus(imc);
606                         ImmReleaseContext(hWnd, imc);
607                         InvalidateRect(hWnd, NULL, TRUE);
608                 }
609                 return( DefWindowProc(hWnd, msg, wp, lp) );
610
611         case WM_SYSCOMMAND:
612                 if(!onSysCommand(hWnd, (DWORD)wp))
613                         return( DefWindowProc(hWnd, msg, wp, lp) );
614                 break;
615         case WM_VSCROLL:
616         case WM_MOUSEWHEEL:
617                 /* throw console window */
618                 PostMessage(gConWnd, msg, wp, lp);
619                 break;
620
621         case WM_IME_CHAR:
622                 PostMessage(gConWnd, msg, wp, lp);
623                 /* break */
624         case WM_CHAR:
625                 selectionClear(hWnd);
626                 break;
627
628         case WM_SYSKEYDOWN:
629         case WM_SYSKEYUP:
630                 if(wp != VK_RETURN) /* alt+enter */
631                         PostMessage(gConWnd, msg, wp, lp);
632                 break;
633         case WM_KEYDOWN:
634         case WM_KEYUP:
635                 if((wp == VK_NEXT || wp == VK_PRIOR ||
636                     wp == VK_HOME || wp == VK_END) &&
637                    (GetKeyState(VK_SHIFT) & 0x8000)) {
638                         if(msg == WM_KEYDOWN) {
639                                 WPARAM  sb = SB_PAGEDOWN;
640                                 if(wp == VK_PRIOR)     sb = SB_PAGEUP;
641                                 else if(wp == VK_HOME) sb = SB_TOP;
642                                 else if(wp == VK_END)  sb = SB_BOTTOM;
643                                 PostMessage(gConWnd, WM_VSCROLL, sb, 0);
644                         }
645                 }
646                 else if(wp == VK_INSERT &&
647                         (GetKeyState(VK_SHIFT) & 0x8000)) {
648                         if(msg == WM_KEYDOWN)
649                                 onPasteFromClipboard(hWnd);
650                 }
651                 else {
652                         PostMessage(gConWnd, msg, wp, lp);
653                 }
654                 break;
655         default:
656                 return( DefWindowProc(hWnd, msg, wp, lp) );
657         }
658         return(1);
659 }
660
661 /*****************************************************************************/
662 #include "option.h"
663
664 /*----------*/
665 static BOOL create_window(ckOpt& opt)
666 {
667         trace("create_window\n");
668
669         HINSTANCE hInstance = GetModuleHandle(NULL);
670         LPWSTR  className = L"CkwWindowClass";
671         const char*     conf_title;
672         LPWSTR  title;
673         WNDCLASSEX wc;
674         DWORD   style = WS_OVERLAPPEDWINDOW;
675         DWORD   exstyle = WS_EX_ACCEPTFILES;
676         LONG    width, height;
677         LONG    posx, posy;
678
679         if(opt.isTranspColor() ||
680            (0 < opt.getTransp() && opt.getTransp() < 255))
681                 exstyle |= WS_EX_LAYERED;
682
683         if(opt.isScrollRight())
684                 exstyle |= WS_EX_RIGHTSCROLLBAR;
685         else
686                 exstyle |= WS_EX_LEFTSCROLLBAR;
687
688         if(opt.isTopMost())
689                 exstyle |= WS_EX_TOPMOST;
690
691         if(opt.isScrollHide() || opt.getSaveLines() < 1)
692                 gVScrollHide = TRUE;
693         else
694                 style |= WS_VSCROLL;
695
696         if(opt.isIconic())
697                 style |= WS_MINIMIZE;
698
699         conf_title = opt.getTitle();
700         if(!conf_title || !conf_title[0]){
701           title = L"ckw";
702         }else{
703           title = new wchar_t[ strlen(conf_title)+1 ];
704           ZeroMemory(title, sizeof(wchar_t) * (strlen(conf_title)+1));
705           MultiByteToWideChar(CP_ACP, 0, conf_title, strlen(conf_title), title, sizeof(wchar_t) * (strlen(conf_title)+1));
706         }
707
708         /* calc window size */
709         CONSOLE_SCREEN_BUFFER_INFO csi;
710         GetConsoleScreenBufferInfo(gStdOut, &csi);
711
712         AdjustWindowRectEx(&gFrame, style, FALSE, exstyle);
713         if(!gVScrollHide)
714                 gFrame.right += GetSystemMetrics(SM_CXVSCROLL);
715
716         gWinW = width  = csi.srWindow.Right  - csi.srWindow.Left + 1;
717         gWinH = height = csi.srWindow.Bottom - csi.srWindow.Top  + 1;
718         width  *= gFontW;
719         height *= gFontH;
720         width  += gBorderSize * 2;
721         height += gBorderSize * 2;
722         width  += gFrame.right  - gFrame.left;
723         height += gFrame.bottom - gFrame.top;
724
725         if(opt.isWinPos()) {
726                 RECT    rc;
727                 SystemParametersInfo(SPI_GETWORKAREA,0,(LPVOID)&rc,0);
728                 posx = opt.getWinPosX();
729                 if(posx < 0) posx = rc.right - (width - posx -1);
730                 else         posx += rc.left;
731                 if(posx < rc.left) posx = rc.left;
732                 if(posx > rc.right-5) posx = rc.right -5;
733                 posy = opt.getWinPosY();
734                 if(posy < 0) posy = rc.bottom - (height - posy -1);
735                 else         posy += rc.top;
736                 if(posy < rc.top) posy = rc.top;
737                 if(posy > rc.bottom-5) posy = rc.bottom -5;
738         }
739         else {
740                 posx = CW_USEDEFAULT;
741                 posy = CW_USEDEFAULT;
742         }
743
744         /**/
745         memset(&wc, 0, sizeof(wc));
746         wc.cbSize = sizeof(wc);
747         wc.style = 0;
748         wc.lpfnWndProc = WndProc;
749         wc.cbClsExtra = 0;
750         wc.cbWndExtra = 0;
751         wc.hInstance = hInstance;
752         wc.hIcon = LoadIcon(hInstance, (LPCTSTR)IDR_ICON);
753         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
754         wc.hbrBackground = CreateSolidBrush(gColorTable[0]);
755         wc.lpszMenuName = NULL;
756         wc.lpszClassName = className;
757         wc.hIconSm = wc.hIcon;
758         if(! RegisterClassEx(&wc))
759                 return(FALSE);
760
761         HWND hWnd = CreateWindowEx(exstyle, className, title, style,
762                                    posx, posy, width, height,
763                                    NULL, NULL, hInstance, NULL);
764         if(!hWnd){
765                 delete [] title;
766                 return(FALSE);
767         }
768
769         sysmenu_init(hWnd);
770
771         if(0 < opt.getTransp() && opt.getTransp() < 255)
772                 SetLayeredWindowAttributes(hWnd, 0, opt.getTransp(), LWA_ALPHA);
773         else if(opt.isTranspColor())
774                 SetLayeredWindowAttributes(hWnd, opt.getTranspColor(), 255, LWA_COLORKEY);
775
776         ShowWindow(hWnd, SW_SHOW);
777         return(TRUE);
778 }
779
780 /*----------*/
781 static BOOL create_child_process(const char* cmd, const char* curdir)
782 {
783         trace("create_child_process\n");
784
785         char* buf = NULL;
786
787         if(!cmd || !cmd[0]) {
788                 buf = new char[32768];
789                 buf[0] = 0;
790                 if(!GetEnvironmentVariableA("COMSPEC", buf, 32768))
791                         strcpy(buf, "cmd.exe");
792         }
793         else {
794                 buf = new char[ strlen(cmd)+1 ];
795                 strcpy(buf, cmd);
796         }
797
798         PROCESS_INFORMATION pi;
799         STARTUPINFOA si;
800         memset(&si, 0, sizeof(si));
801         si.cb = sizeof(si);
802         si.dwFlags = STARTF_USESTDHANDLES;
803         si.hStdInput  = gStdIn;
804         si.hStdOutput = gStdOut;
805         si.hStdError  = gStdErr;
806
807         if (curdir)
808                 if (char *p = strstr((char*)curdir, ":\""))
809                         *(p+1) = '\\';
810
811         if(! CreateProcessA(NULL, buf, NULL, NULL, TRUE,
812                             0, NULL, curdir, &si, &pi)) {
813                 delete [] buf;
814                 return(FALSE);
815         }
816         delete [] buf;
817         CloseHandle(pi.hThread);
818         gChild = pi.hProcess;
819         return(TRUE);
820 }
821
822 /*----------*/
823 static BOOL create_font(const char* name, int height)
824 {
825         trace("create_font\n");
826
827         memset(&gFontLog, 0, sizeof(gFontLog));
828         gFontLog.lfHeight = -height;
829         gFontLog.lfWidth = 0;
830         gFontLog.lfEscapement = 0;
831         gFontLog.lfOrientation = 0;
832         gFontLog.lfWeight = FW_NORMAL;
833         gFontLog.lfItalic = 0;
834         gFontLog.lfUnderline = 0;
835         gFontLog.lfStrikeOut = 0;
836         gFontLog.lfCharSet = DEFAULT_CHARSET;
837         gFontLog.lfOutPrecision = OUT_DEFAULT_PRECIS;
838         gFontLog.lfClipPrecision = CLIP_DEFAULT_PRECIS;
839         gFontLog.lfQuality = DEFAULT_QUALITY;
840         gFontLog.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
841         if(name) {
842                 MultiByteToWideChar(CP_ACP,0, name, -1, gFontLog.lfFaceName, LF_FACESIZE);
843         }
844
845         gFont = CreateFontIndirect(&gFontLog);
846
847         /* calc font size */
848         HDC     hDC = GetDC(NULL);
849         HGDIOBJ oldfont = SelectObject(hDC, gFont);
850         TEXTMETRIC met;
851         INT     width1[26], width2[26], width = 0;
852
853         GetTextMetrics(hDC, &met);
854         GetCharWidth32(hDC, 0x41, 0x5A, width1);
855         GetCharWidth32(hDC, 0x61, 0x7A, width2);
856         SelectObject(hDC, oldfont);
857         ReleaseDC(NULL, hDC);
858
859         for(int i = 0 ; i < 26 ; i++) {
860                 width += width1[i];
861                 width += width2[i];
862         }
863         width /= 26 * 2;
864         gFontW = width; /* met.tmAveCharWidth; */
865         gFontH = met.tmHeight + gLineSpace;
866
867         return(TRUE);
868 }
869
870 /*----------*/
871 static void __hide_alloc_console()
872 {
873         /*
874          * Open Console Window
875          * hack StartupInfo.wShowWindow flag
876          */
877         DWORD*  pflags = (DWORD*) 0x00020068; /* private memory */
878         WORD*   pshow  = (WORD*)  0x0002006C;
879
880         DWORD   backup_flags = *pflags;
881         WORD    backup_show  = *pshow;
882
883         STARTUPINFO si;
884         GetStartupInfo(&si);
885
886         /* check */
887         if(si.dwFlags == backup_flags && si.wShowWindow == backup_show) {
888                 *pflags |= STARTF_USESHOWWINDOW;
889                 *pshow  = SW_HIDE;
890         }
891
892         AllocConsole();
893
894         /* restore */
895         *pflags = backup_flags;
896         *pshow  = backup_show;
897 }
898
899 /*----------*/
900 BOOL WINAPI sig_handler(DWORD n)
901 {
902         return(TRUE);
903 }
904
905 static BOOL create_console(ckOpt& opt)
906 {
907         const char*     conf_title;
908         LPWSTR  title;
909
910         conf_title = opt.getTitle();
911         if(!conf_title || !conf_title[0]){
912           title = L"ckw";
913         }else{
914           title = new wchar_t[ strlen(conf_title)+1 ];
915           ZeroMemory(title, sizeof(wchar_t) * (strlen(conf_title)+1));
916           MultiByteToWideChar(CP_ACP, 0, conf_title, strlen(conf_title), title, sizeof(wchar_t) * (strlen(conf_title)+1));
917         }
918
919         __hide_alloc_console();
920
921         while((gConWnd = GetConsoleWindow()) == NULL) {
922                 Sleep(10);
923         }
924         // \82±\82Ì\83\8b\81[\83v\82ð\92Ç\89Á
925         for(int i=0;!IsWindowVisible(gConWnd) && i<100;i++)
926         {
927           Sleep(10);
928         }
929         while(IsWindowVisible(gConWnd)) {
930                 ShowWindow(gConWnd, SW_HIDE);
931                 Sleep(10);
932         }
933
934         ShowWindow(gConWnd, SW_HIDE);
935
936         SetConsoleTitle(title);
937
938         SetConsoleCtrlHandler(sig_handler, TRUE);
939
940         SECURITY_ATTRIBUTES sa;
941         sa.nLength = sizeof(sa);
942         sa.lpSecurityDescriptor = NULL;
943         sa.bInheritHandle = TRUE;
944
945         gStdIn  = CreateFile(L"CONIN$",  GENERIC_READ|GENERIC_WRITE,
946                              FILE_SHARE_READ|FILE_SHARE_WRITE,
947                              &sa, OPEN_EXISTING, 0, NULL);
948         gStdOut = CreateFile(L"CONOUT$",  GENERIC_READ|GENERIC_WRITE,
949                              FILE_SHARE_READ|FILE_SHARE_WRITE,
950                              &sa, OPEN_EXISTING, 0, NULL);
951         gStdErr = CreateFile(L"CONOUT$",  GENERIC_READ|GENERIC_WRITE,
952                              FILE_SHARE_READ|FILE_SHARE_WRITE,
953                              &sa, OPEN_EXISTING, 0, NULL);
954
955         if(!gConWnd || !gStdIn || !gStdOut || !gStdErr)
956                 return(FALSE);
957
958         HINSTANCE hLib;
959         hLib = LoadLibraryW( L"KERNEL32.DLL" );
960         if (hLib == NULL)
961                 goto done;
962
963         #define GetProc( proc ) \
964         do { \
965                 proc = (proc##T)GetProcAddress( hLib, #proc ); \
966                 if (proc == NULL) \
967                         goto freelib; \
968         } while (0)
969         GetProc( GetConsoleFontInfo );
970         GetProc( GetNumberOfConsoleFonts );
971         GetProc( SetConsoleFont );
972         #undef GetProc
973
974         {
975                 CONSOLE_FONT font[MAX_FONTS];
976                 DWORD fonts;
977                 fonts = GetNumberOfConsoleFonts();
978                 if (fonts > MAX_FONTS)
979                         fonts = MAX_FONTS;
980         
981                 GetConsoleFontInfo(gStdOut, 0, fonts, font);
982                 CONSOLE_FONT minimalFont = { 0, {0, 0}};
983                 for(DWORD i=0;i<fonts;i++){
984                         if(minimalFont.dim.X < font[i].dim.X && minimalFont.dim.Y < font[i].dim.Y)
985                                 minimalFont = font[i];
986                 }
987                 SetConsoleFont(gStdOut, minimalFont.index);
988         }
989         freelib:
990                 FreeLibrary( hLib );
991         done:
992
993         /* set buffer & window size */
994         COORD size;
995         SMALL_RECT sr = {0,0,0,0};
996         SetConsoleWindowInfo(gStdOut, TRUE, &sr);
997         size.X = opt.getWinCharW();
998         size.Y = opt.getWinCharH() + opt.getSaveLines();
999         SetConsoleScreenBufferSize(gStdOut, size);
1000         sr.Left = 0;
1001         sr.Right = opt.getWinCharW()-1;
1002         sr.Top = size.Y - opt.getWinCharH();
1003         sr.Bottom = size.Y-1;
1004         SetConsoleWindowInfo(gStdOut, TRUE, &sr);
1005         size.X = sr.Left;
1006         size.Y = sr.Top;
1007         SetConsoleCursorPosition(gStdOut, size);
1008         return(TRUE);
1009 }
1010
1011 /*----------*/
1012 BOOL init_options(ckOpt& opt)
1013 {
1014         /* create argv */
1015         int     i, argc;
1016         LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1017         char**  argv = new char*[argc+1];
1018         argv[argc] = 0;
1019         for(i = 0 ; i < argc ; i++) {
1020                 DWORD wlen = (DWORD) wcslen(wargv[i]);
1021                 DWORD alen = wlen * 2 + 16;
1022                 argv[i] = new char[alen];
1023                 alen = WideCharToMultiByte(CP_ACP, 0, wargv[i],wlen, argv[i],alen,NULL,NULL);
1024                 argv[i][alen] = 0;
1025         }
1026
1027         opt.loadXdefaults();
1028         bool result = opt.set(argc, argv);
1029
1030         for(i = 0 ; i < argc ; i++)
1031                 delete [] argv[i];
1032         delete [] argv;
1033
1034         if(!result) return(FALSE);
1035
1036         /* set */
1037         for(i = kColor0 ; i <= kColor15 ; i++)
1038                 gColorTable[i] = opt.getColor(i);
1039         gColorTable[kColor7] = opt.getColorFg();
1040         gColorTable[kColor0] = opt.getColorBg();
1041
1042         gColorTable[kColorCursorBg] = opt.getColorCursor();
1043         gColorTable[kColorCursorFg] = ~gColorTable[kColorCursorBg] & 0xFFFFFF;
1044         gColorTable[kColorCursorImeBg] = opt.getColorCursorIme();
1045         gColorTable[kColorCursorImeFg] = ~gColorTable[kColorCursorImeBg] & 0xFFFFFF;
1046
1047         gBorderSize = opt.getBorderSize();
1048         gLineSpace = opt.getLineSpace();
1049
1050         if(opt.getBgBmp()) {
1051                 gBgBmp = (HBITMAP)LoadImageA(NULL, opt.getBgBmp(),
1052                                 IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
1053         }
1054         if(gBgBmp)    gBgBrush = CreatePatternBrush(gBgBmp);
1055         if(!gBgBrush) gBgBrush = CreateSolidBrush(gColorTable[0]);
1056
1057         return(TRUE);
1058 }
1059
1060 /*----------*/
1061 static BOOL initialize()
1062 {
1063         ckOpt opt;
1064
1065         if(! ime_wrap_init()) {
1066                 trace("ime_wrap_init failed\n");
1067         }
1068
1069         if(! init_options(opt)) {
1070                 return(FALSE);
1071         }
1072         if(! create_console(opt)) {
1073                 trace("create_console failed\n");
1074                 return(FALSE);
1075         }
1076         if(! create_font(opt.getFont(), opt.getFontSize())) {
1077                 trace("create_font failed\n");
1078                 return(FALSE);
1079         }
1080         if(! create_child_process(opt.getCmd(), opt.getCurDir())) {
1081                 trace("create_child_process failed\n");
1082                 return(FALSE);
1083         }
1084         if(! create_window(opt)) {
1085                 trace("create_window failed\n");
1086                 return(FALSE);
1087         }
1088
1089         wchar_t path[MAX_PATH+1];
1090         GetSystemDirectory(path, MAX_PATH);
1091         SetCurrentDirectory(path);
1092         return(TRUE);
1093 }
1094
1095 #define SAFE_CloseHandle(handle) \
1096         if(handle) { CloseHandle(handle); handle = NULL; }
1097
1098 #define SAFE_DeleteObject(handle) \
1099         if(handle) { DeleteObject(handle); handle = NULL; }
1100
1101 /*----------*/
1102 static void _terminate()
1103 {
1104         if(gTitle) {
1105                 delete [] gTitle;
1106                 gTitle = NULL;
1107         }
1108         if(gScreen) {
1109                 delete [] gScreen;
1110                 gScreen = NULL;
1111         }
1112         if(gCSI) {
1113                 delete gCSI;
1114                 gCSI = NULL;
1115         }
1116         gConWnd = NULL;
1117         SAFE_CloseHandle(gStdIn);
1118         SAFE_CloseHandle(gStdOut);
1119         SAFE_CloseHandle(gStdErr);
1120         SAFE_CloseHandle(gChild);
1121         SAFE_DeleteObject(gFont);
1122         SAFE_DeleteObject(gBgBrush);
1123         SAFE_DeleteObject(gBgBmp);
1124 }
1125
1126 #ifdef _DEBUG
1127 #include <crtdbg.h>
1128 #endif
1129
1130 /*----------*/
1131 int APIENTRY wWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR lpCmdLine, int nCmdShow)
1132 {
1133 #ifdef _DEBUG
1134         char *a = new char[1];
1135         *a = 0x22;
1136         _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF |
1137                        _CRTDBG_LEAK_CHECK_DF |
1138                        /*_CRTDBG_CHECK_ALWAYS_DF |*/
1139                        _CRTDBG_DELAY_FREE_MEM_DF);
1140         _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
1141         _CrtSetReportMode( _CRT_WARN,   _CRTDBG_MODE_FILE );
1142         _CrtSetReportMode( _CRT_ERROR,  _CRTDBG_MODE_FILE );
1143         _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
1144         _CrtSetReportFile( _CRT_WARN,   _CRTDBG_FILE_STDERR );
1145         _CrtSetReportFile( _CRT_ERROR,  _CRTDBG_FILE_STDERR );
1146 #endif
1147
1148         if(initialize()) {
1149                 MSG msg;
1150                 while(GetMessage(&msg, NULL, 0,0)) {
1151                         TranslateMessage(&msg);
1152                         DispatchMessage(&msg);
1153                 }
1154         }
1155         _terminate();
1156         return(0);
1157 }
1158
1159 /* EOF */