Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / win32_curses / curses_win32.cpp
bloba918830deffb7b1a8737fb75c1abe304ff7d1aa2
1 #ifdef _WIN32
2 #include <windows.h>
3 #else
4 #include "../swell/swell.h"
5 #endif
6 #define CURSES_INSTANCE ___ERRROR_____
8 #include "../wdltypes.h"
9 #include "../wdlutf8.h"
11 #include "curses.h"
13 #include <ctype.h>
14 #include <stdio.h>
16 #define CURSOR_BLINK_TIMER_MS 400
17 #define CURSOR_BLINK_TIMER 2
18 #define CURSOR_BLINK_TIMER_ZEROEVERY 3
20 #define WIN32CURSES_CLASS_NAME "WDLCursesWindow"
22 static void doFontCalc(win32CursesCtx*, HDC);
23 static void reInitializeContext(win32CursesCtx *ctx);
25 static void m_InvalidateArea(win32CursesCtx *ctx, int sx, int sy, int ex, int ey)
27 if (!ctx) return;
29 doFontCalc(ctx,NULL);
31 if (!ctx->m_hwnd || (ctx->need_redraw&4)) return;
33 RECT r;
34 r.left=sx*ctx->m_font_w;
35 r.top=sy*ctx->m_font_h;
36 r.right=ex*ctx->m_font_w;
37 r.bottom=ey*ctx->m_font_h;
38 InvalidateRect(ctx->m_hwnd,&r,FALSE);
41 void __curses_invalidatefull(win32CursesCtx *inst, bool finish)
43 if (inst && inst->m_hwnd)
45 if (finish)
47 if (inst->need_redraw&4)
49 inst->need_redraw&=~4;
50 InvalidateRect(inst->m_hwnd,NULL,FALSE);
53 else
54 inst->need_redraw|=4;
58 void __addnstr(win32CursesCtx *ctx, const char *str,int n)
60 if (!ctx||n==0) return;
62 const int sx=ctx->m_cursor_x, sy=ctx->m_cursor_y, cols=ctx->cols;
63 if (!ctx->m_framebuffer || sy < 0 || sy >= ctx->lines || sx < 0 || sx >= cols) return;
64 win32CursesFB *p=ctx->m_framebuffer + (sx + sy*cols);
66 const unsigned char attr = ctx->m_cur_attr;
67 while (n && *str)
69 int c,sz=wdl_utf8_parsechar(str,&c);
70 p->c=(wchar_t)c;
71 p->attr=attr;
72 p++;
73 str+=sz;
74 if (n > 0 && (n-=sz)<0) n = 0;
76 if (++ctx->m_cursor_x >= cols) break;
78 m_InvalidateArea(ctx,sx,sy,sy < ctx->m_cursor_y ? cols : ctx->m_cursor_x+1,ctx->m_cursor_y+1);
81 void __addnstr_w(win32CursesCtx *ctx, const wchar_t *str,int n)
83 if (!ctx||n==0) return;
85 const int sx=ctx->m_cursor_x, sy=ctx->m_cursor_y, cols=ctx->cols;
86 if (!ctx->m_framebuffer || sy < 0 || sy >= ctx->lines || sx < 0 || sx >= cols) return;
87 win32CursesFB *p=ctx->m_framebuffer + (sx + sy*cols);
89 const unsigned char attr = ctx->m_cur_attr;
90 while (n-- && *str)
92 p->c=*str++;
93 p->attr=attr;
94 p++;
95 if (++ctx->m_cursor_x >= cols) break;
97 m_InvalidateArea(ctx,sx,sy,sy < ctx->m_cursor_y ? cols : ctx->m_cursor_x+1,ctx->m_cursor_y+1);
100 void __clrtoeol(win32CursesCtx *ctx)
102 if (!ctx) return;
104 if (ctx->m_cursor_x<0)ctx->m_cursor_x=0;
105 int n = ctx->cols - ctx->m_cursor_x;
106 if (!ctx->m_framebuffer || ctx->m_cursor_y < 0 || ctx->m_cursor_y >= ctx->lines || n < 1) return;
107 win32CursesFB *p=ctx->m_framebuffer + (ctx->m_cursor_x + ctx->m_cursor_y*ctx->cols);
108 int sx=ctx->m_cursor_x;
109 while (n--)
111 p->c=0;
112 p->attr=ctx->m_cur_erase_attr;
113 p++;
115 m_InvalidateArea(ctx,sx,ctx->m_cursor_y,ctx->cols,ctx->m_cursor_y+1);
118 void __curses_erase(win32CursesCtx *ctx)
120 if (!ctx) return;
122 ctx->m_cur_attr=0;
123 ctx->m_cur_erase_attr=0;
124 if (ctx->m_framebuffer) memset(ctx->m_framebuffer,0,ctx->cols*ctx->lines*sizeof(*ctx->m_framebuffer));
125 ctx->m_cursor_x=0;
126 ctx->m_cursor_y=0;
127 m_InvalidateArea(ctx,0,0,ctx->cols,ctx->lines);
130 void __move(win32CursesCtx *ctx, int y, int x, int noupdest)
132 if (!ctx) return;
134 m_InvalidateArea(ctx,ctx->m_cursor_x,ctx->m_cursor_y,ctx->m_cursor_x+1,ctx->m_cursor_y+1);
135 ctx->m_cursor_x=wdl_max(x,0);
136 ctx->m_cursor_y=wdl_max(y,0);
137 if (!noupdest) m_InvalidateArea(ctx,ctx->m_cursor_x,ctx->m_cursor_y,ctx->m_cursor_x+1,ctx->m_cursor_y+1);
141 void __init_pair(win32CursesCtx *ctx, int pair, int fcolor, int bcolor)
143 if (!ctx || pair < 0 || pair >= COLOR_PAIRS) return;
145 pair=COLOR_PAIR(pair);
146 fcolor &= RGB(255,255,255);
147 bcolor &= RGB(255,255,255);
149 ctx->colortab[pair][0]=fcolor;
150 ctx->colortab[pair][1]=bcolor;
152 if (fcolor & 0xff) fcolor|=0xff;
153 if (fcolor & 0xff00) fcolor|=0xff00;
154 if (fcolor & 0xff0000) fcolor|=0xff0000;
155 ctx->colortab[pair|A_BOLD][0]=fcolor;
156 ctx->colortab[pair|A_BOLD][1]=bcolor;
159 int *curses_win32_global_user_colortab;
161 static LRESULT xlateKey(int msg, WPARAM wParam, LPARAM lParam)
163 if (msg == WM_KEYDOWN)
165 #ifndef _WIN32
166 if (lParam & FVIRTKEY)
167 #endif
168 switch (wParam)
170 case VK_HOME: return KEY_HOME;
171 case VK_UP: return KEY_UP;
172 case VK_PRIOR: return KEY_PPAGE;
173 case VK_LEFT: return KEY_LEFT;
174 case VK_RIGHT: return KEY_RIGHT;
175 case VK_END: return KEY_END;
176 case VK_DOWN: return KEY_DOWN;
177 case VK_NEXT: return KEY_NPAGE;
178 case VK_INSERT: return KEY_IC;
179 case VK_DELETE: return KEY_DC;
180 case VK_F1: return KEY_F1;
181 case VK_F2: return KEY_F2;
182 case VK_F3: return KEY_F3;
183 case VK_F4: return KEY_F4;
184 case VK_F5: return KEY_F5;
185 case VK_F6: return KEY_F6;
186 case VK_F7: return KEY_F7;
187 case VK_F8: return KEY_F8;
188 case VK_F9: return KEY_F9;
189 case VK_F10: return KEY_F10;
190 case VK_F11: return KEY_F11;
191 case VK_F12: return KEY_F12;
192 #ifndef _WIN32
193 case VK_SUBTRACT: return '-'; // numpad -
194 case VK_ADD: return '+';
195 case VK_MULTIPLY: return '*';
196 case VK_DIVIDE: return '/';
197 case VK_DECIMAL: return '.';
198 case VK_NUMPAD0: return '0';
199 case VK_NUMPAD1: return '1';
200 case VK_NUMPAD2: return '2';
201 case VK_NUMPAD3: return '3';
202 case VK_NUMPAD4: return '4';
203 case VK_NUMPAD5: return '5';
204 case VK_NUMPAD6: return '6';
205 case VK_NUMPAD7: return '7';
206 case VK_NUMPAD8: return '8';
207 case VK_NUMPAD9: return '9';
208 case (32768|VK_RETURN): return VK_RETURN;
209 #endif
212 switch (wParam)
214 case VK_RETURN: case VK_BACK: case VK_TAB: case VK_ESCAPE: return wParam;
215 case VK_CONTROL: break;
217 default:
218 if(GetAsyncKeyState(VK_CONTROL)&0x8000)
220 if (wParam>='a' && wParam<='z')
222 wParam += 1-'a';
223 return wParam;
225 if (wParam>='A' && wParam<='Z')
227 wParam += 1-'A';
228 return wParam;
230 if ((wParam&~0x80) == '[') return 27;
231 if ((wParam&~0x80) == ']') return 29;
236 #ifdef _WIN32 // todo : fix for nonwin32
237 if (msg == WM_CHAR)
239 if(wParam>=32) return wParam;
241 #else
242 //osx/linux
243 if (wParam >= 32)
245 if (!(GetAsyncKeyState(VK_SHIFT)&0x8000))
247 if (wParam>='A' && wParam<='Z')
249 if ((GetAsyncKeyState(VK_LWIN)&0x8000)) wParam -= 'A'-1;
250 else
251 wParam += 'a'-'A';
254 return wParam;
257 #endif
258 return ERR;
262 static void m_reinit_framebuffer(win32CursesCtx *ctx)
264 if (!ctx) return;
266 doFontCalc(ctx,NULL);
267 RECT r;
269 GetClientRect(ctx->m_hwnd,&r);
271 ctx->lines=r.bottom / ctx->m_font_h;
272 ctx->cols=r.right / ctx->m_font_w;
273 if (ctx->lines<1) ctx->lines=1;
274 if (ctx->cols<1) ctx->cols=1;
275 ctx->m_cursor_x=0;
276 ctx->m_cursor_y=0;
277 free(ctx->m_framebuffer);
278 ctx->m_framebuffer=(win32CursesFB *)malloc(sizeof(win32CursesFB)*ctx->lines*ctx->cols);
279 if (ctx->m_framebuffer) memset(ctx->m_framebuffer, 0,sizeof(win32CursesFB)*ctx->lines*ctx->cols);
281 const int *tab = ctx->user_colortab ? ctx->user_colortab : curses_win32_global_user_colortab;
282 if (tab)
284 ctx->user_colortab_lastfirstval=tab[0];
285 for (int x=0;x<COLOR_PAIRS;x++) __init_pair(ctx,x,tab[x*2],tab[x*2+1]);
288 #ifndef WM_MOUSEWHEEL
289 #define WM_MOUSEWHEEL 0x20A
290 #endif
292 LRESULT CALLBACK cursesWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
294 win32CursesCtx *ctx = (win32CursesCtx*)GetWindowLongPtr(hwnd,GWLP_USERDATA);
296 #ifdef _WIN32
298 static int Scroll_Message;
299 if (!Scroll_Message)
301 Scroll_Message = (int)RegisterWindowMessage("MSWHEEL_ROLLMSG");
302 if (!Scroll_Message) Scroll_Message=-1;
304 if (Scroll_Message > 0 && uMsg == (UINT)Scroll_Message)
306 uMsg=WM_MOUSEWHEEL;
307 wParam<<=16;
309 #endif
311 if (ctx) switch (uMsg)
313 case WM_DESTROY:
314 ctx->m_hwnd=0;
315 return 0;
316 case WM_CHAR: case WM_KEYDOWN:
318 #ifdef __APPLE__
320 int f=0;
321 wParam = SWELL_MacKeyToWindowsKeyEx(NULL,&f,1);
322 lParam=f;
324 #endif
327 const int a=(int)xlateKey(uMsg,wParam,lParam);
328 if (a != ERR)
330 const int qsize = sizeof(ctx->m_kb_queue)/sizeof(ctx->m_kb_queue[0]);
331 if (ctx->m_kb_queue_valid>=qsize) // queue full, dump an old event!
333 ctx->m_kb_queue_valid--;
334 ctx->m_kb_queue_pos++;
337 ctx->m_kb_queue[(ctx->m_kb_queue_pos + ctx->m_kb_queue_valid++) & (qsize-1)] = a;
340 case WM_KEYUP:
341 return 0;
342 case WM_GETMINMAXINFO:
344 LPMINMAXINFO p=(LPMINMAXINFO)lParam;
345 p->ptMinTrackSize.x = 160;
346 p->ptMinTrackSize.y = 120;
348 return 0;
349 case WM_SIZE:
350 if (wParam != SIZE_MINIMIZED)
352 m_reinit_framebuffer(ctx);
353 if (ctx->do_update) ctx->do_update(ctx);
354 else ctx->need_redraw|=1;
356 return 0;
357 case WM_RBUTTONDOWN:
358 case WM_LBUTTONDOWN:
359 SetFocus(hwnd);
360 case WM_LBUTTONUP:
361 case WM_RBUTTONUP:
362 case WM_CAPTURECHANGED:
363 case WM_MOUSEMOVE:
364 case WM_MOUSEWHEEL:
365 case WM_LBUTTONDBLCLK:
366 case WM_RBUTTONDBLCLK:
367 case WM_MBUTTONDBLCLK:
368 if (ctx && ctx->fontsize_ptr && uMsg == WM_MOUSEWHEEL && (GetAsyncKeyState(VK_CONTROL)&0x8000))
370 int a = (int)(short)HIWORD(wParam);
371 if (a<0 && *ctx->fontsize_ptr > 4) (*ctx->fontsize_ptr)--;
372 else if (a>=0 && *ctx->fontsize_ptr < 64) (*ctx->fontsize_ptr)++;
373 else return 1;
375 if (ctx->mOurFont)
377 DeleteObject(ctx->mOurFont);
378 ctx->mOurFont=NULL;
380 reInitializeContext(ctx);
381 m_reinit_framebuffer(ctx);
382 if (ctx->do_update) ctx->do_update(ctx);
383 else ctx->need_redraw|=1;
385 return 1;
387 if (ctx && ctx->onMouseMessage) return ctx->onMouseMessage(ctx->user_data,hwnd,uMsg,wParam,lParam);
388 return 0;
390 case WM_SETCURSOR:
391 if (ctx->m_font_w && ctx->m_font_h)
393 POINT p;
394 GetCursorPos(&p);
395 ScreenToClient(hwnd, &p);
396 p.x /= ctx->m_font_w;
397 p.y /= ctx->m_font_h;
399 const int topmarg=ctx->scrollbar_topmargin;
400 const int bottmarg=ctx->scrollbar_botmargin;
401 int paney[2] = { topmarg, ctx->div_y+topmarg+1 };
402 int paneh[2] = { ctx->div_y, ctx->lines-ctx->div_y-topmarg-bottmarg-1 };
403 bool has_panes=(ctx->div_y < ctx->lines-topmarg-bottmarg-1);
404 int scrollw[2] = { ctx->cols-ctx->drew_scrollbar[0], ctx->cols-ctx->drew_scrollbar[1] };
406 if (has_panes && p.y >= ctx->div_y+1 && p.y < ctx->div_y+2) SetCursor(LoadCursor(NULL, IDC_SIZENS));
407 else if (p.y < 1 || p.y >= ctx->lines-1) SetCursor(LoadCursor(NULL, IDC_ARROW));
408 else if (p.y >= paney[0] && p.y < paney[0]+paneh[0] && p.x >= scrollw[0]) SetCursor(LoadCursor(NULL, IDC_ARROW));
409 else if (p.y >= paney[1] && p.y < paney[1]+paneh[1] && p.x >= scrollw[1]) SetCursor(LoadCursor(NULL, IDC_ARROW));
410 else SetCursor(LoadCursor(NULL, IDC_IBEAM));
411 return TRUE;
414 #ifdef _WIN32
415 case WM_GETDLGCODE:
416 if (GetParent(hwnd))
418 return DLGC_WANTALLKEYS;
420 return 0;
421 #endif
422 case WM_TIMER:
423 if (wParam==CURSOR_BLINK_TIMER && ctx)
425 const char la = ctx->cursor_state;
426 ctx->cursor_state = (ctx->cursor_state+1)%CURSOR_BLINK_TIMER_ZEROEVERY;
428 const int *tab = ctx->user_colortab ? ctx->user_colortab : curses_win32_global_user_colortab;
429 if (tab && tab[0] != ctx->user_colortab_lastfirstval)
431 m_reinit_framebuffer(ctx);
432 if (ctx->do_update) ctx->do_update(ctx);
433 else ctx->need_redraw|=1;
435 else if (!!ctx->cursor_state != !!la)
437 __move(ctx,ctx->m_cursor_y,ctx->m_cursor_x,1);// refresh cursor
440 return 0;
441 case WM_CREATE:
443 // this only is called on osx or from standalone, it seems, since on win32 ctx isnt set up yet
444 ctx->m_hwnd=hwnd;
445 #ifndef _WIN32
446 m_reinit_framebuffer(ctx);
447 ctx->need_redraw|=1;
448 #endif
449 SetTimer(hwnd,CURSOR_BLINK_TIMER,CURSOR_BLINK_TIMER_MS,NULL);
450 return 0;
451 case WM_ERASEBKGND:
452 return 1;
453 case WM_PAINT:
456 PAINTSTRUCT ps;
457 HDC hdc=BeginPaint(hwnd,&ps);
458 if (hdc)
460 const int topmarg=ctx->scrollbar_topmargin;
461 const int bottmarg=ctx->scrollbar_botmargin;
462 int paney[2] = { topmarg, ctx->div_y+topmarg+1 };
463 int paneh[2] = { ctx->div_y, ctx->lines-ctx->div_y-topmarg-bottmarg-1 };
464 bool has_panes=(ctx->div_y < ctx->lines-topmarg-bottmarg-1);
465 if (!has_panes) paneh[0]++;
467 ctx->drew_scrollbar[0]=ctx->drew_scrollbar[1]=0;
468 if (ctx->want_scrollbar > 0)
470 RECT cr;
471 GetClientRect(hwnd, &cr);
472 double cf=(double)cr.right/(double)ctx->m_font_w-(double)ctx->cols;
473 int ws=ctx->want_scrollbar;
474 if (cf < 0.5) ++ws;
476 int i;
477 for (i=0; i < 2; ++i)
479 ctx->scroll_y[i]=ctx->scroll_h[i]=0;
480 if (paneh[i] > 0 && ctx->tot_y > paneh[i])
482 ctx->drew_scrollbar[i]=ws;
483 int ey=paneh[i]*ctx->m_font_h;
484 ctx->scroll_h[i]=ey*paneh[i]/ctx->tot_y;
485 if (ctx->scroll_h[i] < ctx->m_font_h) ctx->scroll_h[i]=ctx->m_font_h;
486 ctx->scroll_y[i]=(ey-ctx->scroll_h[i])*ctx->offs_y[i]/(ctx->tot_y-paneh[i]);
487 if (ctx->scroll_y[i] < 0) ctx->scroll_y[i]=0;
488 if (ctx->scroll_y[i] > ey-ctx->scroll_h[i]) ctx->scroll_y[i]=ey-ctx->scroll_h[i];
493 RECT r = ps.rcPaint;
494 doFontCalc(ctx,ps.hdc);
496 HGDIOBJ oldf=SelectObject(hdc,ctx->mOurFont);
497 int y,ypos;
498 int lattr=-1;
499 #ifdef _WIN32
500 SetTextAlign(hdc,TA_TOP|TA_LEFT);
501 #endif
502 const win32CursesFB *ptr=(const win32CursesFB*)ctx->m_framebuffer;
503 RECT updr=r;
505 r.left /= ctx->m_font_w;
506 r.top /= ctx->m_font_h;
507 r.bottom += ctx->m_font_h-1;
508 r.bottom /= ctx->m_font_h;
509 r.right += ctx->m_font_w-1;
510 r.right /= ctx->m_font_w;
512 if (r.top < 0) r.top=0;
513 if (r.bottom > ctx->lines) r.bottom=ctx->lines;
514 if (r.left < 0) r.left=0;
515 if (r.right > ctx->cols) r.right=ctx->cols;
517 ypos = r.top * ctx->m_font_h;
518 ptr += (r.top * ctx->cols);
521 HBRUSH bgbrushes[COLOR_PAIRS << NUM_ATTRBITS];
522 for(y=0;y<sizeof(bgbrushes)/sizeof(bgbrushes[0]);y++) bgbrushes[y] = CreateSolidBrush(ctx->colortab[y][1]);
524 char cstate=ctx->cursor_state;
525 if (ctx->m_cursor_y != ctx->cursor_state_ly || ctx->m_cursor_x != ctx->cursor_state_lx)
527 ctx->cursor_state_lx=ctx->m_cursor_x;
528 ctx->cursor_state_ly=ctx->m_cursor_y;
529 ctx->cursor_state=0;
530 cstate=1;
533 if (ctx->m_framebuffer) for (y = r.top; y < r.bottom; y ++, ypos+=ctx->m_font_h, ptr += ctx->cols)
535 int x = r.left,xpos = r.left * ctx->m_font_w;
537 const win32CursesFB *p = ptr + r.left;
539 int defer_blanks=0;
541 int right=r.right;
542 if (y >= paney[0] && y < paney[0]+paneh[0])
544 right=wdl_min(right, ctx->cols-ctx->drew_scrollbar[0]);
546 else if (y >= paney[1] && y < paney[1]+paneh[1])
548 right=wdl_min(right, ctx->cols-ctx->drew_scrollbar[1]);
551 for (;; x ++, xpos+=ctx->m_font_w, p ++)
553 wchar_t c=' ';
554 int attr=0;
556 if (x < right)
558 c=p->c;
559 attr=p->attr;
562 const bool isCursor = cstate && y == ctx->m_cursor_y && x == ctx->m_cursor_x;
563 const bool isNotBlank = c>=128 || (isprint(c) && !isspace(c));
565 if (defer_blanks > 0 && (isNotBlank || isCursor || attr != lattr || x>=right))
567 RECT tr={xpos - defer_blanks*ctx->m_font_w,ypos,xpos,ypos+ctx->m_font_h};
568 FillRect(hdc,&tr,bgbrushes[lattr&((COLOR_PAIRS << NUM_ATTRBITS)-1)]);
569 defer_blanks=0;
572 if (x>=right) break;
574 if (isCursor && ctx->cursor_type == WIN32_CURSES_CURSOR_TYPE_BLOCK)
576 SetTextColor(hdc,ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][1]);
577 SetBkColor(hdc,ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][0]);
578 lattr = -1;
580 else
581 if (attr != lattr)
583 SetTextColor(hdc,ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][0]);
584 SetBkColor(hdc,ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][1]);
585 lattr=attr;
588 if (isNotBlank||isCursor)
590 #ifdef _WIN32
591 int txpos = xpos;
592 TextOutW(hdc,txpos,ypos,isNotBlank ? &c : L" ",1);
593 #else
594 const int max_charw = ctx->m_font_w, max_charh = ctx->m_font_h;
595 RECT tr={xpos,ypos,xpos+max_charw, ypos+max_charh};
596 HBRUSH br=bgbrushes[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)];
597 if (isCursor && ctx->cursor_type == WIN32_CURSES_CURSOR_TYPE_BLOCK)
599 br = CreateSolidBrush(ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][0]);
600 FillRect(hdc,&tr,br);
601 DeleteObject(br);
603 else
605 FillRect(hdc,&tr,br);
607 char tmp[16];
608 if (c >= 128)
610 WDL_MakeUTFChar(tmp,c,sizeof(tmp));
612 else
614 tmp[0]=isNotBlank ? (char)c : ' ';
615 tmp[1]=0;
617 DrawText(hdc,tmp,-1,&tr,DT_LEFT|DT_TOP|DT_NOPREFIX|DT_NOCLIP);
618 #endif
620 if (isCursor && ctx->cursor_type != WIN32_CURSES_CURSOR_TYPE_BLOCK)
622 RECT r={xpos,ypos,xpos+2,ypos+ctx->m_font_h};
623 if (ctx->cursor_type == WIN32_CURSES_CURSOR_TYPE_HORZBAR)
625 RECT tr={xpos,ypos+ctx->m_font_h-2,xpos+ctx->m_font_w,ypos+ctx->m_font_h};
626 r=tr;
628 HBRUSH br=CreateSolidBrush(ctx->colortab[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)][0]);
629 FillRect(hdc,&r,br);
630 DeleteObject(br);
633 else
635 defer_blanks++;
640 int ex=ctx->cols*ctx->m_font_w;
641 int ey=ctx->lines*ctx->m_font_h;
643 int anyscrollw=wdl_max(ctx->drew_scrollbar[0], ctx->drew_scrollbar[1]);
644 if (anyscrollw && updr.right >= ex-anyscrollw*ctx->m_font_w)
646 HBRUSH sb1=CreateSolidBrush(RGB(128,128,128));
647 HBRUSH sb2=CreateSolidBrush(RGB(96, 96, 96));
648 int i;
649 for (i=0; i < 2; ++i)
651 if (ctx->drew_scrollbar[i])
653 int scrolly=paney[i]*ctx->m_font_h+ctx->scroll_y[i];
654 int scrollh=ctx->scroll_h[i];
655 RECT tr = { ex-ctx->drew_scrollbar[i]*ctx->m_font_w, paney[i]*ctx->m_font_h, updr.right, wdl_min(scrolly, updr.bottom) };
656 if (tr.bottom > tr.top) FillRect(hdc, &tr, sb1);
657 tr.top=wdl_max(updr.top, scrolly);
658 tr.bottom=wdl_min(updr.bottom, scrolly+scrollh);
659 if (tr.bottom > tr.top) FillRect(hdc, &tr, sb2);
660 tr.top=wdl_max(updr.top,scrolly+scrollh);
661 tr.bottom=(paney[i]+paneh[i])*ctx->m_font_h;
662 if (tr.bottom > tr.top) FillRect(hdc, &tr, sb1);
665 DeleteObject(sb1);
666 DeleteObject(sb2);
669 ex -= ctx->m_font_w;
670 if (updr.right >= ex)
672 // draw the scrollbars if they haven't been already drawn
673 if (!ctx->drew_scrollbar[0] && updr.bottom > paney[0]*ctx->m_font_h && updr.top < (paney[0]+paneh[0])*ctx->m_font_h)
675 RECT tr = { ex, paney[0]*ctx->m_font_h, updr.right, (paney[0]+paneh[0])*ctx->m_font_h };
676 FillRect(hdc, &tr, bgbrushes[0]);
678 if (!ctx->drew_scrollbar[1] && updr.bottom > paney[1]*ctx->m_font_h && updr.top < (paney[1]+paneh[1])*ctx->m_font_h)
680 RECT tr = { ex, paney[1]*ctx->m_font_h, updr.right, (paney[1]+paneh[1])*ctx->m_font_h };
681 FillRect(hdc, &tr, bgbrushes[0]);
684 // draw line endings of special areas
686 const int div1a = has_panes ? (paney[0]+paneh[0]) : 0;
687 const int div1b = has_panes ? paney[1] : 0;
689 int y;
690 const int bm1 = ctx->lines-bottmarg;
691 const int fonth = ctx->m_font_h;
692 for (y = r.top; y < r.bottom; y ++)
694 if (y < topmarg || y>=bm1 || (y<div1b && y >= div1a))
696 const int attr = ctx->m_framebuffer ? ctx->m_framebuffer[(y+1) * ctx->cols - 1].attr : 0; // last attribute of line
698 const int yp = y * fonth;
699 RECT tr = { wdl_max(ex,updr.left), wdl_max(yp,updr.top), updr.right, wdl_min(yp+fonth,updr.bottom) };
700 FillRect(hdc, &tr, bgbrushes[attr&((COLOR_PAIRS << NUM_ATTRBITS)-1)]);
705 if (updr.bottom > ey)
707 RECT tr= { updr.left, wdl_max(ey,updr.top), updr.right, updr.bottom };
708 FillRect(hdc, &tr, bgbrushes[2]);
711 for(y=0;y<sizeof(bgbrushes)/sizeof(bgbrushes[0]);y++) DeleteObject(bgbrushes[y]);
712 SelectObject(hdc,oldf);
714 EndPaint(hwnd,&ps);
718 return 0;
720 return DefWindowProc(hwnd,uMsg,wParam,lParam);
723 static void doFontCalc(win32CursesCtx *ctx, HDC hdcIn)
725 if (!ctx || !ctx->m_hwnd || !(ctx->need_redraw&2)) return;
727 HDC hdc = hdcIn;
728 if (!hdc) hdc = GetDC(ctx->m_hwnd);
730 if (!hdc) return;
732 ctx->need_redraw&=~2;
734 HGDIOBJ oldf=SelectObject(hdc,ctx->mOurFont);
735 TEXTMETRIC tm;
736 GetTextMetrics(hdc,&tm);
737 ctx->m_font_h=tm.tmHeight;
738 ctx->m_font_w=tm.tmAveCharWidth;
739 SelectObject(hdc,oldf);
741 if (hdc != hdcIn) ReleaseDC(ctx->m_hwnd,hdc);
745 void reInitializeContext(win32CursesCtx *ctx)
747 if (!ctx) return;
749 if (!ctx->mOurFont) ctx->mOurFont = CreateFont(
750 ctx->fontsize_ptr ? *ctx->fontsize_ptr :
751 #ifdef _WIN32
753 #else
755 #endif
756 0, // width
757 0, // escapement
758 0, // orientation
759 #ifndef __APPLE__
760 FW_NORMAL, // normal
761 #else
762 FW_BOLD,
763 #endif
764 FALSE, //italic
765 FALSE, //undelrine
766 FALSE, //strikeout
767 ANSI_CHARSET,
768 OUT_DEFAULT_PRECIS,
769 CLIP_DEFAULT_PRECIS,
770 ANTIALIASED_QUALITY, //NONANTIALIASED_QUALITY,//DEFAULT_QUALITY,
771 #ifdef _WIN32
772 FF_MODERN,
773 #else
775 #endif
776 "Courier New");
778 ctx->need_redraw|=2;
779 ctx->m_font_w=8;
780 ctx->m_font_h=8;
781 doFontCalc(ctx,NULL);
787 void __initscr(win32CursesCtx *ctx)
789 #ifdef WDL_IS_FAKE_CURSES
790 if (!curses_win32_global_user_colortab && (!ctx || !ctx->user_colortab))
791 #endif
793 __init_pair(ctx,0,RGB(192,192,192),RGB(0,0,0));
797 void __endwin(win32CursesCtx *ctx)
799 if (ctx)
801 if (ctx->m_hwnd)
802 curses_setWindowContext(ctx->m_hwnd,0);
803 ctx->m_kb_queue_valid=0;
804 ctx->m_hwnd=0;
805 free(ctx->m_framebuffer);
806 ctx->m_framebuffer=0;
807 if (ctx->mOurFont) DeleteObject(ctx->mOurFont);
808 ctx->mOurFont=0;
813 int curses_getch(win32CursesCtx *ctx)
815 if (!ctx || !ctx->m_hwnd) return ERR;
817 #ifdef _WIN32
818 if (ctx->want_getch_runmsgpump>0)
820 MSG msg;
821 if (ctx->want_getch_runmsgpump>1)
823 while(!ctx->m_kb_queue_valid && GetMessage(&msg,NULL,0,0))
825 TranslateMessage(&msg);
826 DispatchMessage(&msg);
829 else while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
831 TranslateMessage(&msg);
832 DispatchMessage(&msg);
835 #endif
837 if (ctx->m_kb_queue_valid)
839 const int qsize = sizeof(ctx->m_kb_queue)/sizeof(ctx->m_kb_queue[0]);
840 const int a = ctx->m_kb_queue[ctx->m_kb_queue_pos & (qsize-1)];
841 ctx->m_kb_queue_pos++;
842 ctx->m_kb_queue_valid--;
843 return a;
846 if (ctx->need_redraw&1)
848 ctx->need_redraw&=~1;
849 InvalidateRect(ctx->m_hwnd,NULL,FALSE);
850 return 'L'-'A'+1;
853 return ERR;
856 void curses_setWindowContext(HWND hwnd, win32CursesCtx *ctx)
858 SetWindowLongPtr(hwnd,GWLP_USERDATA,(INT_PTR)ctx);
859 if (ctx)
861 ctx->m_hwnd=hwnd;
862 ctx->m_kb_queue_valid=0;
864 free(ctx->m_framebuffer);
865 ctx->m_framebuffer=0;
867 SetTimer(hwnd,CURSOR_BLINK_TIMER,CURSOR_BLINK_TIMER_MS,NULL);
868 reInitializeContext(ctx);
869 m_reinit_framebuffer(ctx);
870 InvalidateRect(hwnd,NULL,FALSE);
874 #ifdef _WIN32
875 static int m_regcnt;
876 #endif
878 void curses_unregisterChildClass(HINSTANCE hInstance)
880 #ifdef _WIN32
881 if (!--m_regcnt)
882 UnregisterClass(WIN32CURSES_CLASS_NAME,hInstance);
883 #endif
886 void curses_registerChildClass(HINSTANCE hInstance)
888 #ifdef _WIN32
889 if (!m_regcnt++)
891 WNDCLASS wc={CS_DBLCLKS,};
892 wc.lpfnWndProc = cursesWindowProc;
893 wc.hInstance = hInstance;
894 wc.hCursor = LoadCursor(NULL,IDC_ARROW);
895 wc.lpszClassName = WIN32CURSES_CLASS_NAME;
897 RegisterClass(&wc);
899 #endif
902 #ifndef _WIN32
903 HWND curses_ControlCreator(HWND parent, const char *cname, int idx, const char *classname, int style, int x, int y, int w, int h)
905 HWND hw=0;
906 if (!strcmp(classname,WIN32CURSES_CLASS_NAME))
908 hw=CreateDialog(NULL,0,parent,(DLGPROC)cursesWindowProc);
911 if (hw)
913 SetWindowLong(hw,GWL_ID,idx);
914 SetWindowPos(hw,HWND_TOP,x,y,w,h,SWP_NOZORDER|SWP_NOACTIVATE);
915 ShowWindow(hw,SW_SHOWNA);
916 return hw;
919 return 0;
922 #endif
924 HWND curses_CreateWindow(HINSTANCE hInstance, win32CursesCtx *ctx, const char *title)
926 if (!ctx) return NULL;
927 #ifdef _WIN32
928 ctx->m_hwnd = CreateWindowEx(0,WIN32CURSES_CLASS_NAME, title,WS_CAPTION|WS_MAXIMIZEBOX|WS_MINIMIZEBOX|WS_SIZEBOX|WS_SYSMENU,
929 CW_USEDEFAULT,CW_USEDEFAULT,640,480,
930 NULL, NULL,hInstance,NULL);
931 #else
932 ctx->m_hwnd = CreateDialog(NULL,0,NULL,(DLGPROC)cursesWindowProc);
934 #endif
935 if (ctx->m_hwnd)
937 curses_setWindowContext(ctx->m_hwnd,ctx);
938 ShowWindow(ctx->m_hwnd,SW_SHOW);
940 return ctx->m_hwnd;