5 #include "../swell/swell.h"
7 #define CURSES_INSTANCE ___ERRROR_____
14 #define CURSOR_BLINK_TIMER_MS 400
15 #define CURSOR_BLINK_TIMER 2
16 #define CURSOR_BLINK_TIMER_ZEROEVERY 3
18 #ifndef WIN32_CURSES_CURSORTYPE
19 #define WIN32_CURSES_CURSORTYPE 1 // 1 for vertical bar, 2 for horz bar, 0 for block
21 #define WIN32CURSES_CLASS_NAME "WDLCursesWindow"
23 #define WIN32_CONSOLE_KBQUEUE
25 static void doFontCalc(win32CursesCtx
*, HDC
);
27 static void m_InvalidateArea(win32CursesCtx
*ctx
, int sx
, int sy
, int ex
, int ey
)
32 r
.left
=sx
*ctx
->m_font_w
;
33 r
.top
=sy
*ctx
->m_font_h
;
34 r
.right
=ex
*ctx
->m_font_w
;
35 r
.bottom
=ey
*ctx
->m_font_h
;
36 if (ctx
->m_hwnd
) InvalidateRect(ctx
->m_hwnd
,&r
,FALSE
);
39 void __addnstr(win32CursesCtx
*ctx
, const char *str
,int n
)
41 if (ctx
->m_cursor_x
<0)
43 int skip
= -ctx
->m_cursor_x
;
50 int slen
= strlen(str
);
51 str
+= min(slen
,skip
);
54 int sx
=ctx
->m_cursor_x
;
55 int sy
=ctx
->m_cursor_y
;
56 if (n
==0||!ctx
->m_framebuffer
|| ctx
->m_cursor_y
< 0 || ctx
->m_cursor_y
>= ctx
->lines
) return;
57 char *p
=(char *)ctx
->m_framebuffer
+ 2*(ctx
->m_cursor_x
+ ctx
->m_cursor_y
*ctx
->cols
);
63 if (++ctx
->m_cursor_x
>= ctx
->cols
)
67 if (ctx
->m_cursor_y
>= ctx
->lines
) { ctx
->m_cursor_y
=ctx
->lines
-1; ctx
->m_cursor_x
=ctx
->cols
-1; break; }
70 m_InvalidateArea(ctx
,sx
,sy
,sy
< ctx
->m_cursor_y
? ctx
->cols
: ctx
->m_cursor_x
+1,ctx
->m_cursor_y
+1);
73 void __clrtoeol(win32CursesCtx
*ctx
)
75 if (ctx
->m_cursor_x
<0)ctx
->m_cursor_x
=0;
76 int n
= ctx
->cols
- ctx
->m_cursor_x
;
77 if (!ctx
->m_framebuffer
|| ctx
->m_cursor_y
< 0 || ctx
->m_cursor_y
>= ctx
->lines
|| n
< 1) return;
78 char *p
=(char *)ctx
->m_framebuffer
+ 2*(ctx
->m_cursor_x
+ ctx
->m_cursor_y
*ctx
->cols
);
79 int sx
=ctx
->m_cursor_x
;
83 p
[1]=ctx
->m_cur_erase_attr
;
86 m_InvalidateArea(ctx
,sx
,ctx
->m_cursor_y
,ctx
->cols
,ctx
->m_cursor_y
+1);
89 void __curses_erase(win32CursesCtx
*ctx
)
92 ctx
->m_cur_erase_attr
=0;
93 if (ctx
->m_framebuffer
) memset(ctx
->m_framebuffer
,0,ctx
->cols
*ctx
->lines
*2);
96 m_InvalidateArea(ctx
,0,0,ctx
->cols
,ctx
->lines
);
99 void __move(win32CursesCtx
*ctx
, int x
, int y
, int noupdest
)
101 m_InvalidateArea(ctx
,ctx
->m_cursor_x
,ctx
->m_cursor_y
,ctx
->m_cursor_x
+1,ctx
->m_cursor_y
+1);
104 if (!noupdest
) m_InvalidateArea(ctx
,ctx
->m_cursor_x
,ctx
->m_cursor_y
,ctx
->m_cursor_x
+1,ctx
->m_cursor_y
+1);
108 void __init_pair(win32CursesCtx
*ctx
, int pair
, int fcolor
, int bcolor
)
110 if (pair
< 0 || pair
>= COLOR_PAIRS
) return;
112 pair
=COLOR_PAIR(pair
);
114 ctx
->colortab
[pair
][0]=fcolor
;
115 ctx
->colortab
[pair
][1]=bcolor
;
117 if (fcolor
& 0xff) fcolor
|=0xff;
118 if (fcolor
& 0xff00) fcolor
|=0xff00;
119 if (fcolor
& 0xff0000) fcolor
|=0xff0000;
120 ctx
->colortab
[pair
|A_BOLD
][0]=fcolor
;
121 ctx
->colortab
[pair
|A_BOLD
][1]=bcolor
;
125 static int xlateKey(int msg
, int wParam
, int lParam
)
127 if (msg
== WM_KEYDOWN
)
130 if (lParam
& FVIRTKEY
)
134 case VK_HOME
: return KEY_HOME
;
135 case VK_UP
: return KEY_UP
;
136 case VK_PRIOR
: return KEY_PPAGE
;
137 case VK_LEFT
: return KEY_LEFT
;
138 case VK_RIGHT
: return KEY_RIGHT
;
139 case VK_END
: return KEY_END
;
140 case VK_DOWN
: return KEY_DOWN
;
141 case VK_NEXT
: return KEY_NPAGE
;
142 case VK_INSERT
: return KEY_IC
;
143 case VK_DELETE
: return KEY_DC
;
144 case VK_F1
: return KEY_F1
;
145 case VK_F2
: return KEY_F2
;
146 case VK_F3
: return KEY_F3
;
147 case VK_F4
: return KEY_F4
;
148 case VK_F5
: return KEY_F5
;
149 case VK_F6
: return KEY_F6
;
150 case VK_F7
: return KEY_F7
;
151 case VK_F8
: return KEY_F8
;
152 case VK_F9
: return KEY_F9
;
153 case VK_F10
: return KEY_F10
;
154 case VK_F11
: return KEY_F11
;
155 case VK_F12
: return KEY_F12
;
157 case VK_SUBTRACT
: return (GetAsyncKeyState(VK_SHIFT
)&0x8000)?'_':'-'; // numpad -
163 case VK_RETURN
: case VK_BACK
: case VK_TAB
: case VK_ESCAPE
: return wParam
;
164 case VK_CONTROL
: break;
167 if(GetAsyncKeyState(VK_CONTROL
)&0x8000)
169 if (wParam
>='a' && wParam
<='z')
174 if (wParam
>='A' && wParam
<='Z')
183 #ifdef _WIN32 // todo : fix for nonwin32
186 if(wParam
>=32) return wParam
;
193 if (!(GetAsyncKeyState(VK_SHIFT
)&0x8000))
195 if (wParam
>='A' && wParam
<='Z')
197 if ((GetAsyncKeyState(VK_MENU
)&0x8000)) wParam
-= 'A'-1;
204 if (wParam
=='-') wParam
='_';
205 else if (wParam
>='0' && wParam
<='9')
207 if (wParam
=='0') wParam
= ')';
208 else if (wParam
=='1') wParam
= '!';
209 else if (wParam
=='2') wParam
= '@';
210 else if (wParam
=='3') wParam
= '#';
211 else if (wParam
=='4') wParam
= '$';
212 else if (wParam
=='5') wParam
= '%';
213 else if (wParam
=='6') wParam
= '^';
214 else if (wParam
=='7') wParam
= '&';
215 else if (wParam
=='8') wParam
= '*';
216 else if (wParam
=='9') wParam
= '(';
227 static void m_reinit_framebuffer(win32CursesCtx
*ctx
)
229 doFontCalc(ctx
,NULL
);
232 GetClientRect(ctx
->m_hwnd
,&r
);
234 ctx
->lines
=r
.bottom
/ ctx
->m_font_h
;
235 ctx
->cols
=r
.right
/ ctx
->m_font_w
;
236 if (ctx
->lines
<1) ctx
->lines
=1;
237 if (ctx
->cols
<1) ctx
->cols
=1;
240 ctx
->m_framebuffer
=(unsigned char *)realloc(ctx
->m_framebuffer
,2*ctx
->lines
*ctx
->cols
);
241 if (ctx
->m_framebuffer
) memset(ctx
->m_framebuffer
,0,2*ctx
->lines
*ctx
->cols
);
243 #ifndef WM_MOUSEWHEEL
244 #define WM_MOUSEWHEEL 0x20A
247 LRESULT CALLBACK
cursesWindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
249 win32CursesCtx
*ctx
= (win32CursesCtx
*)GetWindowLongPtr(hwnd
,GWLP_USERDATA
);
253 static int Scroll_Message
;
256 Scroll_Message
= (int)RegisterWindowMessage("MSWHEEL_ROLLMSG");
257 if (!Scroll_Message
) Scroll_Message
=-1;
259 if (Scroll_Message
> 0 && uMsg
== (UINT
)Scroll_Message
)
266 if (ctx
)switch (uMsg
)
271 #ifdef WIN32_CONSOLE_KBQUEUE
272 case WM_CHAR
: case WM_KEYDOWN
:
275 int a
=xlateKey(uMsg
,wParam
,lParam
);
276 if (a
!= ERR
&& ctx
->m_kbq
)
278 ctx
->m_kbq
->Add(&a
,sizeof(int));
284 case WM_GETMINMAXINFO
:
286 LPMINMAXINFO p
=(LPMINMAXINFO
)lParam
;
287 p
->ptMinTrackSize
.x
= 160;
288 p
->ptMinTrackSize
.y
= 120;
292 if (wParam
!= SIZE_MINIMIZED
)
294 m_reinit_framebuffer(ctx
);
295 ctx
->m_need_redraw
=1;
303 case WM_CAPTURECHANGED
:
306 if (ctx
&& ctx
->onMouseMessage
) return ctx
->onMouseMessage(ctx
->user_data
,hwnd
,uMsg
,wParam
,lParam
);
312 return DLGC_WANTALLKEYS
;
317 if (wParam
==CURSOR_BLINK_TIMER
&& ctx
)
319 int la
= ctx
->m_cursor_state
;
320 ctx
->m_cursor_state
= (ctx
->m_cursor_state
+1)%CURSOR_BLINK_TIMER_ZEROEVERY
;
322 if (!!ctx
->m_cursor_state
!= !!la
)
323 __move(ctx
,ctx
->m_cursor_y
,ctx
->m_cursor_x
,1);// refresh cursor
325 #ifdef WIN32_CONSOLE_KBQUEUE
331 if (ctx
->ui_run
) ctx
->ui_run(ctx
);
339 // this only is called on osx or from standalone, it seems, since on win32 ctx isnt set up yet
341 #ifdef WIN32_CONSOLE_KBQUEUE
342 SetTimer(hwnd
,1,33,NULL
);
345 m_reinit_framebuffer(ctx
);
346 ctx
->m_need_redraw
=1;
348 SetTimer(hwnd
,CURSOR_BLINK_TIMER
,CURSOR_BLINK_TIMER_MS
,NULL
);
356 if (GetUpdateRect(hwnd
,&r
,FALSE
))
358 GetClientRect(hwnd
,&r
);
362 HDC hdc
=BeginPaint(hwnd
,&ps
);
365 doFontCalc(ctx
,ps
.hdc
);
367 HGDIOBJ oldf
=SelectObject(hdc
,ctx
->mOurFont
);
371 SetTextAlign(hdc
,TA_TOP
|TA_LEFT
);
373 const char *ptr
=(const char*)ctx
->m_framebuffer
;
376 r
.left
/= ctx
->m_font_w
;
377 r
.top
/= ctx
->m_font_h
;
378 r
.bottom
+= ctx
->m_font_h
-1;
379 r
.bottom
/= ctx
->m_font_h
;
380 r
.right
+= ctx
->m_font_w
-1;
381 r
.right
/= ctx
->m_font_w
;
383 ypos
= r
.top
* ctx
->m_font_h
;
384 ptr
+= 2*(r
.top
* ctx
->cols
);
386 if (r
.top
< 0) r
.top
=0;
387 if (r
.bottom
> ctx
->lines
) r
.bottom
=ctx
->lines
;
388 if (r
.left
< 0) r
.left
=0;
389 if (r
.right
> ctx
->cols
) r
.right
=ctx
->cols
;
391 HBRUSH bgbrushes
[COLOR_PAIRS
<< NUM_ATTRBITS
];
392 for(y
=0;y
<sizeof(bgbrushes
)/sizeof(bgbrushes
[0]);y
++) bgbrushes
[y
] = CreateSolidBrush(ctx
->colortab
[y
][1]);
394 int cstate
=ctx
->m_cursor_state
;
395 if (ctx
->m_cursor_y
!= ctx
->m_cursor_state_ly
||
396 ctx
->m_cursor_x
!= ctx
->m_cursor_state_lx
)
398 ctx
->m_cursor_state_lx
=ctx
->m_cursor_x
;
399 ctx
->m_cursor_state_ly
=ctx
->m_cursor_y
;
400 ctx
->m_cursor_state
=0;
404 if (ctx
->m_framebuffer
) for (y
= r
.top
; y
< r
.bottom
; y
++, ypos
+=ctx
->m_font_h
, ptr
+= ctx
->cols
*2)
406 int x
= r
.left
,xpos
= r
.left
* ctx
->m_font_w
;
408 const char *p
= ptr
+ r
.left
*2;
412 for (;; x
++, xpos
+=ctx
->m_font_w
, p
+= 2)
422 bool isCursor
= cstate
&& y
== ctx
->m_cursor_y
&& x
== ctx
->m_cursor_x
;
423 bool isNotBlank
= (isprint(c
) && !isspace(c
));
425 if (defer_blanks
> 0 && (isNotBlank
|| isCursor
|| attr
!= lattr
|| x
>=r
.right
))
427 RECT tr
={xpos
- defer_blanks
*ctx
->m_font_w
,ypos
,xpos
,ypos
+ctx
->m_font_h
};
428 FillRect(hdc
,&tr
,bgbrushes
[lattr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)]);
432 if (x
>=r
.right
) break;
434 #if WIN32_CURSES_CURSORTYPE == 0
437 SetTextColor(hdc
,ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][1]);
438 SetBkColor(hdc
,ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][0]);
442 #endif // WIN32_CURSES_CURSORTYPE == 0
445 SetTextColor(hdc
,ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][0]);
446 SetBkColor(hdc
,ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][1]);
450 if (isNotBlank
||isCursor
)
454 TextOut(hdc
,txpos
,ypos
,isNotBlank
? p
: " ",1);
456 RECT tr
={xpos
,ypos
,xpos
+32,ypos
+32};
457 HBRUSH br
=bgbrushes
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)];
458 #if WIN32_CURSES_CURSORTYPE == 0
461 br
= CreateSolidBrush(ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][0]);
462 FillRect(hdc
,&tr
,br
);
468 FillRect(hdc
,&tr
,br
);
471 DrawText(hdc
,isprint(c
) && !isspace(c
) ?tmp
: " ",-1,&tr
,DT_LEFT
|DT_TOP
|DT_NOPREFIX
|DT_NOCLIP
);
473 #if WIN32_CURSES_CURSORTYPE > 0
476 #if WIN32_CURSES_CURSORTYPE == 1
477 RECT r
={xpos
,ypos
,xpos
+2,ypos
+ctx
->m_font_h
};
478 #elif WIN32_CURSES_CURSORTYPE == 2
479 RECT r
={xpos
,ypos
+ctx
->m_font_h
-2,xpos
+ctx
->m_font_w
,ypos
+ctx
->m_font_h
};
481 HBRUSH br
=CreateSolidBrush(ctx
->colortab
[attr
&((COLOR_PAIRS
<< NUM_ATTRBITS
)-1)][0]);
493 int rm
=ctx
->cols
* ctx
->m_font_w
;
494 int bm
=ctx
->lines
* ctx
->m_font_h
;
495 if (updr
.right
>= rm
)
497 RECT tr
={max(rm
,updr
.left
),max(updr
.top
,0),updr
.right
,updr
.bottom
};
498 FillRect(hdc
,&tr
,bgbrushes
[0]);
500 if (updr
.bottom
>= bm
)
502 RECT tr
={max(0,updr
.left
),max(updr
.top
,bm
),updr
.right
,updr
.bottom
};
503 FillRect(hdc
,&tr
,bgbrushes
[0]);
506 for(y
=0;y
<sizeof(bgbrushes
)/sizeof(bgbrushes
[0]);y
++) DeleteObject(bgbrushes
[y
]);
507 SelectObject(hdc
,oldf
);
515 return DefWindowProc(hwnd
,uMsg
,wParam
,lParam
);
518 static void doFontCalc(win32CursesCtx
*ctx
, HDC hdcIn
)
520 if (!ctx
|| !ctx
->m_hwnd
|| !ctx
->m_need_fontcalc
) return;
523 if (!hdc
) hdc
= GetDC(ctx
->m_hwnd
);
527 ctx
->m_need_fontcalc
=false;
528 HGDIOBJ oldf
=SelectObject(hdc
,ctx
->mOurFont
);
530 GetTextMetrics(hdc
,&tm
);
531 ctx
->m_font_h
=tm
.tmHeight
;
532 ctx
->m_font_w
=tm
.tmAveCharWidth
;
533 SelectObject(hdc
,oldf
);
535 if (hdc
!= hdcIn
) ReleaseDC(ctx
->m_hwnd
,hdc
);
539 static void reInitializeContext(win32CursesCtx
*ctx
)
541 if (!ctx
->mOurFont
) ctx
->mOurFont
= CreateFont(
565 NONANTIALIASED_QUALITY
,//DEFAULT_QUALITY,
573 ctx
->m_need_fontcalc
=true;
576 doFontCalc(ctx
,NULL
);
582 void __initscr(win32CursesCtx
*ctx
)
584 __init_pair(ctx
,0,RGB(192,192,192),RGB(0,0,0));
587 void __endwin(win32CursesCtx
*ctx
)
590 curses_setWindowContext(ctx
->m_hwnd
,0);
592 free(ctx
->m_framebuffer
);
593 ctx
->m_framebuffer
=0;
596 if (ctx
->mOurFont
) DeleteObject(ctx
->mOurFont
);
602 int curses_getch(win32CursesCtx
*ctx
)
604 if (!ctx
->m_hwnd
) return ERR
;
607 #ifndef WIN32_CONSOLE_KBQUEUE
608 // if we're suppose to run the message pump ourselves (optional!)
610 while(PeekMessage(&msg
,NULL
,0,0,PM_REMOVE
))
612 TranslateMessage(&msg
);
613 int a
=xlateKey(msg
.message
,msg
.wParam
,msg
.lParam
);
614 if (a
!= ERR
) return a
;
616 DispatchMessage(&msg
);
622 if (ctx
->want_getch_runmsgpump
>0)
625 if (ctx
->want_getch_runmsgpump
>1)
627 while(!(ctx
->m_kbq
&& ctx
->m_kbq
->Available()>=(int)sizeof(int)) && GetMessage(&msg
,NULL
,0,0))
629 TranslateMessage(&msg
);
630 DispatchMessage(&msg
);
633 else while(PeekMessage(&msg
,NULL
,0,0,PM_REMOVE
))
635 TranslateMessage(&msg
);
636 DispatchMessage(&msg
);
641 if (ctx
->m_kbq
&& ctx
->m_kbq
->Available()>=(int)sizeof(int))
643 int a
=*(int *) ctx
->m_kbq
->Get();
644 ctx
->m_kbq
->Advance(sizeof(int));
645 ctx
->m_kbq
->Compact();
650 if (ctx
->m_need_redraw
)
652 ctx
->m_need_redraw
=0;
653 InvalidateRect(ctx
->m_hwnd
,NULL
,FALSE
);
660 void curses_setWindowContext(HWND hwnd
, win32CursesCtx
*ctx
)
662 SetWindowLongPtr(hwnd
,GWLP_USERDATA
,(INT_PTR
)ctx
);
667 ctx
->m_kbq
=new WDL_Queue
;
669 free(ctx
->m_framebuffer
);
670 ctx
->m_framebuffer
=0;
672 SetTimer(hwnd
,CURSOR_BLINK_TIMER
,CURSOR_BLINK_TIMER_MS
,NULL
);
673 reInitializeContext(ctx
);
674 m_reinit_framebuffer(ctx
);
675 InvalidateRect(hwnd
,NULL
,FALSE
);
680 void curses_unregisterChildClass(HINSTANCE hInstance
)
684 UnregisterClass(WIN32CURSES_CLASS_NAME
,hInstance
);
688 void curses_registerChildClass(HINSTANCE hInstance
)
694 wc
.lpfnWndProc
= cursesWindowProc
;
695 wc
.hInstance
= hInstance
;
696 wc
.hCursor
= LoadCursor(NULL
,IDC_ARROW
);
697 wc
.lpszClassName
= WIN32CURSES_CLASS_NAME
;
705 HWND
curses_ControlCreator(HWND parent
, const char *cname
, int idx
, const char *classname
, int style
, int x
, int y
, int w
, int h
)
708 if (!strcmp(classname
,WIN32CURSES_CLASS_NAME
))
710 hw
=CreateDialog(NULL
,0,parent
,(DLGPROC
)cursesWindowProc
);
715 SetWindowLong(hw
,GWL_ID
,idx
);
716 SetWindowPos(hw
,HWND_TOP
,x
,y
,w
,h
,SWP_NOZORDER
|SWP_NOACTIVATE
);
717 ShowWindow(hw
,SW_SHOWNA
);
726 HWND
curses_CreateWindow(HINSTANCE hInstance
, win32CursesCtx
*ctx
, const char *title
)
729 ctx
->m_hwnd
= CreateWindowEx(0,WIN32CURSES_CLASS_NAME
, title
,WS_CAPTION
|WS_MAXIMIZEBOX
|WS_MINIMIZEBOX
|WS_SIZEBOX
|WS_SYSMENU
,
730 CW_USEDEFAULT
,CW_USEDEFAULT
,640,480,
731 NULL
, NULL
,hInstance
,NULL
);
733 ctx
->m_hwnd
= CreateDialog(NULL
,0,NULL
,(DLGPROC
)cursesWindowProc
);
738 curses_setWindowContext(ctx
->m_hwnd
,ctx
);
739 ShowWindow(ctx
->m_hwnd
,SW_SHOW
);