ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /emu/Nt/win.c/
#define Unknown WUnknown
#define Colormap WColormap
#define Cursor WCursor
#define Display WDisplay
#define Drawable WDrawable
#define Font WFont
#define GC WGC
#define Point WPoint
#define Rectangle WRectangle
#define Screen WScreen
#define Visual WVisual
#define Window WWindow
#include <windows.h>
#undef Colormap
#undef Cursor
#undef Display
#undef XDrawable
#undef Font
#undef GC
#undef Point
#undef Rectangle
#undef Screen
#undef Visual
#undef Window
#undef Unknown
#include "dat.h"
#include "fns.h"
#include "error.h"
#include <draw.h>
#include "keyboard.h"
#include "cursor.h"
#include "r16.h"
extern ulong displaychan;
extern int bytesperline(Rectangle, int);
extern int main(int argc, char **argv);
static void dprint(char*, ...);
static DWORD WINAPI winproc(LPVOID);
static HINSTANCE inst;
static HINSTANCE previnst;
static int cmdshow;
static HWND window;
static HDC screen;
static HPALETTE palette;
static int maxxsize;
static int maxysize;
static int attached;
static int isunicode = 1;
static HCURSOR hcursor;
char *argv0 = "inferno";
static ulong *data;
extern DWORD PlatformId;
char* gkscanid = "emu_win32vk";
int WINAPI
WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
{
inst = winst;
previnst = wprevinst;
cmdshow = wcmdshow;
/* cmdline passed into WinMain does not contain name of executable.
* The globals __argc and __argv to include this info - like UNIX
*/
main(__argc, __argv);
return 0;
}
static void
dprint(char *fmt, ...)
{
va_list arg;
char buf[128];
va_start(arg, fmt);
vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
va_end(arg);
OutputDebugString("inferno: ");
OutputDebugString(buf);
}
static void
graphicscmap(PALETTEENTRY *pal)
{
int r, g, b, cr, cg, cb, v, p;
int num, den;
int i, j;
for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
den=r;
if(g>den) den=g;
if(b>den) den=b;
if(den==0) /* divide check -- pick grey shades */
cr=cg=cb=v*17;
else{
num=17*(4*den+v);
cr=r*num/den;
cg=g*num/den;
cb=b*num/den;
}
p = i+(j&15);
pal[p].peRed = cr*0x01010101;
pal[p].peGreen = cg*0x01010101;
pal[p].peBlue = cb*0x01010101;
pal[p].peFlags = 0;
}
}
}
static void
graphicsgmap(PALETTEENTRY *pal, int d)
{
int i, j, s, m, p;
s = 8-d;
m = 1;
while(--d >= 0)
m *= 2;
m = 255/(m-1);
for(i=0; i < 256; i++){
j = (i>>s)*m;
p = 255-i;
pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101;
pal[p].peFlags = 0;
}
}
static ulong
autochan(void)
{
HDC dc;
int bpp;
dc = GetDC(NULL);
if (dc == NULL)
return CMAP8;
bpp = GetDeviceCaps(dc, BITSPIXEL);
if (bpp < 15)
return CMAP8;
if (bpp < 24)
return RGB15;
if (bpp < 32)
return RGB24;
return XRGB32;
}
uchar*
attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
{
int i, k;
ulong c;
DWORD h;
RECT bs;
RGBQUAD *rgb;
HBITMAP bits;
BITMAPINFO *bmi;
LOGPALETTE *logpal;
PALETTEENTRY *pal;
int bsh, bsw, sx, sy;
if(attached)
goto Return;
/* Compute bodersizes */
memset(&bs, 0, sizeof(bs));
AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0);
bsw = bs.right - bs.left;
bsh = bs.bottom - bs.top;
sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw;
Xsize -= Xsize % 4; /* Round down */
if(Xsize > sx)
Xsize = sx;
sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20;
if(Ysize > sy)
Ysize = sy;
logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY));
if(logpal == nil)
return nil;
logpal->palVersion = 0x300;
logpal->palNumEntries = 256;
pal = logpal->palPalEntry;
c = displaychan;
if(c == 0)
c = autochan();
k = 8;
if(TYPE(c) == CGrey){
graphicsgmap(pal, NBITS(c));
c = GREY8;
}else{
if(c == RGB15)
k = 16;
else if(c == RGB24)
k = 24;
else if(c == XRGB32)
k = 32;
else
c = CMAP8;
graphicscmap(pal);
}
palette = CreatePalette(logpal);
if(k == 8)
bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
else
bmi = malloc(sizeof(BITMAPINFOHEADER));
if(bmi == nil)
return nil;
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi->bmiHeader.biWidth = Xsize;
bmi->bmiHeader.biHeight = -Ysize; /* - => origin upper left */
bmi->bmiHeader.biPlanes = 1; /* always 1 */
bmi->bmiHeader.biBitCount = k;
bmi->bmiHeader.biCompression = BI_RGB;
bmi->bmiHeader.biSizeImage = 0; /* Xsize*Ysize*(k/8) */
bmi->bmiHeader.biXPelsPerMeter = 0;
bmi->bmiHeader.biYPelsPerMeter = 0;
bmi->bmiHeader.biClrUsed = 0;
bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */
if(k == 8){
rgb = bmi->bmiColors;
for(i = 0; i < 256; i++){
rgb[i].rgbRed = pal[i].peRed;
rgb[i].rgbGreen = pal[i].peGreen;
rgb[i].rgbBlue = pal[i].peBlue;
}
}
screen = CreateCompatibleDC(NULL);
if(screen == nil){
fprint(2, "screen dc nil\n");
return nil;
}
if(SelectPalette(screen, palette, 1) == nil){
fprint(2, "select pallete failed\n");
}
i = RealizePalette(screen);
GdiFlush();
bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0);
if(bits == nil){
fprint(2, "CreateDIBSection failed\n");
return nil;
}
SelectObject(screen, bits);
GdiFlush();
CreateThread(0, 16384, winproc, nil, 0, &h);
attached = 1;
Return:
r->min.x = 0;
r->min.y = 0;
r->max.x = Xsize;
r->max.y = Ysize;
displaychan = c;
*chan = c;
*d = k;
*width = (Xsize/4)*(k/8);
*softscreen = 1;
return (uchar*)data;
}
void
flushmemscreen(Rectangle r)
{
RECT wr;
if(r.max.x<=r.min.x || r.max.y<=r.min.y)
return;
wr.left = r.min.x;
wr.top = r.min.y;
wr.right = r.max.x;
wr.bottom = r.max.y;
InvalidateRect(window, &wr, 0);
}
static void
scancode(WPARAM wparam, LPARAM lparam, int keyup)
{
uchar buf[2];
if(!(lparam & (1<<30))) { /* don't auto-repeat chars */
buf[0] = wparam;
buf[1] = wparam >> 8;
if (keyup)
buf[1] |= 0x80;
qproduce(gkscanq, buf, sizeof buf);
}
}
LRESULT CALLBACK
WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT paint;
HDC hdc;
LPMINMAXINFO mmi;
LONG x, y, w, h, b;
HCURSOR dcurs;
POINT m;
switch(msg) {
case WM_SETCURSOR:
/* User set */
if(hcursor != NULL) {
SetCursor(hcursor);
break;
}
/* Pick the default */
dcurs = LoadCursor(NULL, IDC_ARROW);
SetCursor(dcurs);
break;
case WM_MOUSEWHEEL:
if((int)wparam>0)
b = 8;
else
b = 16;
m.x = LOWORD(lparam);
m.y = HIWORD(lparam);
ScreenToClient(hwnd, &m);
goto mok;
case WM_LBUTTONDBLCLK:
b = (1<<8) | 1;
goto process;
case WM_MBUTTONDBLCLK:
b = (1<<8) | 2;
goto process;
case WM_RBUTTONDBLCLK:
b = (1<<8) | 4;
goto process;
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
b = 0;
process:
m.x = LOWORD(lparam);
m.y = HIWORD(lparam);
mok:
if(wparam & MK_LBUTTON)
b |= 1;
if(wparam & MK_MBUTTON)
b |= 2;
if(wparam & MK_RBUTTON) {
if(wparam & MK_CONTROL)
b |= 2; //simulate middle button
else
b |= 4; //right button
}
mousetrack(b, m.x, m.y, 0);
break;
case WM_SYSKEYDOWN:
if(gkscanq)
scancode(wparam, lparam, 0);
break;
case WM_SYSKEYUP:
if(gkscanq)
scancode(wparam, lparam, 1);
else if(wparam == VK_MENU)
gkbdputc(gkbdq, Latin);
break;
case WM_KEYDOWN:
if(gkscanq) {
scancode(wparam, lparam, 0);
break;
}
switch(wparam) {
default:
return 0;
case VK_HOME:
wparam = Home;
break;
case VK_END:
wparam = End;
break;
case VK_UP:
wparam = Up;
break;
case VK_DOWN:
wparam = Down;
break;
case VK_LEFT:
wparam = Left;
break;
case VK_RIGHT:
wparam = Right;
break;
case VK_PRIOR: /* VK_PAGE_UP */
wparam = Pgup;
break;
case VK_NEXT: /* VK_PAGE_DOWN */
wparam = Pgdown;
break;
case VK_PRINT:
wparam = Print;
break;
case VK_SCROLL:
wparam = Scroll;
break;
case VK_PAUSE:
wparam = Pause;
break;
case VK_INSERT:
wparam = Ins;
break;
case VK_DELETE:
wparam = Del;
break;
/*
case VK_TAB:
if(GetKeyState(VK_SHIFT)<0)
wparam = BackTab;
else
wparam = '\t';
break;
*/
}
gkbdputc(gkbdq, wparam);
break;
case WM_KEYUP:
if(gkscanq)
scancode(wparam, lparam, 1);
break;
case WM_CHAR:
if(gkscanq)
break;
switch(wparam) {
case '\n':
wparam = '\r';
break;
case '\r':
wparam = '\n';
break;
case '\t':
if(GetKeyState(VK_SHIFT)<0)
wparam = BackTab;
else
wparam = '\t';
break;
}
if(lparam & KF_ALTDOWN)
wparam = APP | (wparam & 0xFF);
gkbdputc(gkbdq, wparam);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
cleanexit(0);
break;
case WM_PALETTECHANGED:
if((HWND)wparam == hwnd)
break;
/* fall through */
case WM_QUERYNEWPALETTE:
hdc = GetDC(hwnd);
SelectPalette(hdc, palette, 0);
if(RealizePalette(hdc) != 0)
InvalidateRect(hwnd, nil, 0);
ReleaseDC(hwnd, hdc);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &paint);
SelectPalette(hdc, palette, 0);
RealizePalette(hdc);
x = paint.rcPaint.left;
y = paint.rcPaint.top;
w = paint.rcPaint.right - x;
h = paint.rcPaint.bottom - y;
BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
EndPaint(hwnd, &paint);
break;
case WM_GETMINMAXINFO:
mmi = (LPMINMAXINFO)lparam;
mmi->ptMaxSize.x = maxxsize;
mmi->ptMaxSize.y = maxysize;
mmi->ptMaxTrackSize.x = maxxsize;
mmi->ptMaxTrackSize.y = maxysize;
break;
case WM_SYSCHAR:
case WM_COMMAND:
case WM_CREATE:
case WM_SETFOCUS:
case WM_DEVMODECHANGE:
case WM_WININICHANGE:
case WM_INITMENU:
default:
if(isunicode)
return DefWindowProcW(hwnd, msg, wparam, lparam);
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
return 0;
}
static DWORD WINAPI
winproc(LPVOID x)
{
MSG msg;
RECT size;
WNDCLASSW wc;
WNDCLASSA wca;
DWORD ws;
if(!previnst){
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = inst;
wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
wc.hCursor = NULL;
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"inferno";
if(RegisterClassW(&wc) == 0){
wca.style = wc.style;
wca.lpfnWndProc = wc.lpfnWndProc;
wca.cbClsExtra = wc.cbClsExtra;
wca.cbWndExtra = wc.cbWndExtra;
wca.hInstance = wc.hInstance;
wca.hIcon = wc.hIcon;
wca.hCursor = wc.hCursor;
wca.hbrBackground = wc.hbrBackground;
wca.lpszMenuName = 0;
wca.lpszClassName = "inferno";
isunicode = 0;
RegisterClassA(&wca);
}
}
size.left = 0;
size.top = 0;
size.right = Xsize;
size.bottom = Ysize;
ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
if(AdjustWindowRect(&size, ws, 0)) {
maxxsize = size.right - size.left;
maxysize = size.bottom - size.top;
}else{
maxxsize = Xsize + 40;
maxysize = Ysize + 40;
}
if(isunicode) {
window = CreateWindowExW(
0, /* extended style */
L"inferno", /* class */
L"Inferno", /* caption */
ws, /* style */
CW_USEDEFAULT, /* init. x pos */
CW_USEDEFAULT, /* init. y pos */
maxxsize, /* init. x size */
maxysize, /* init. y size */
NULL, /* parent window (actually owner window for overlapped) */
NULL, /* menu handle */
inst, /* program handle */
NULL /* create parms */
);
}else{
window = CreateWindowExA(
0, /* extended style */
"inferno", /* class */
"Inferno", /* caption */
ws, /* style */
CW_USEDEFAULT, /* init. x pos */
CW_USEDEFAULT, /* init. y pos */
maxxsize, /* init. x size */
maxysize, /* init. y size */
NULL, /* parent window (actually owner window for overlapped) */
NULL, /* menu handle */
inst, /* program handle */
NULL /* create parms */
);
}
if(window == nil){
fprint(2, "can't make window\n");
ExitThread(0);
}
SetForegroundWindow(window);
ShowWindow(window, cmdshow);
UpdateWindow(window);
// CloseWindow(window);
if(isunicode) {
while(GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}else{
while(GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
attached = 0;
ExitThread(msg.wParam);
return 0;
}
void
setpointer(int x, int y)
{
POINT pt;
pt.x = x; pt.y = y;
ClientToScreen(window, &pt);
SetCursorPos(pt.x, pt.y);
}
void
drawcursor(Drawcursor* c)
{
HCURSOR nh, oh;
Rectangle ir;
int i, h, j, bpl, ch, cw;
uchar *bs, *bc, *and, *xor, *cand, *cxor;
/* Set the default system cursor */
if(c->data == nil) {
oh = hcursor;
hcursor = NULL;
if(oh != NULL) {
SendMessage(window, WM_SETCURSOR, (int)window, 0);
DestroyCursor(oh);
}
return;
}
ir.min.x = c->minx;
ir.min.y = c->miny;
ir.max.x = c->maxx;
ir.max.y = c->maxy;
bpl = bytesperline(ir, 1);
h = (c->maxy-c->miny)/2;
ch = GetSystemMetrics(SM_CYCURSOR);
cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
i = ch*cw;
and = malloc(2*i);
if(and == nil)
return;
xor = and + i;
memset(and, 0xff, i);
memset(xor, 0, i);
cand = and;
cxor = xor;
bc = c->data;
bs = c->data + h*bpl;
for(i = 0; i < ch && i < h; i++) {
for(j = 0; j < cw && j < bpl; j++) {
cand[j] = ~(bs[j] | bc[j]);
cxor[j] = ~bs[j] & bc[j];
}
cand += cw;
cxor += cw;
bs += bpl;
bc += bpl;
}
nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
if(nh != NULL) {
oh = hcursor;
hcursor = nh;
SendMessage(window, WM_SETCURSOR, (int)window, 0);
if(oh != NULL)
DestroyCursor(oh);
}else{
print("CreateCursor error %d\n", GetLastError());
print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
}
free(and);
}
/*
* thanks to drawterm for these
*/
static char*
clipreadunicode(HANDLE h)
{
Rune16 *p;
int n;
char *q;
p = GlobalLock(h);
n = rune16nlen(p, runes16len(p)+1);
q = malloc(n);
if(q != nil)
runes16toutf(q, p, n);
GlobalUnlock(h);
if(q == nil)
error(Enovmem);
return q;
}
static char *
clipreadutf(HANDLE h)
{
uchar *p;
p = GlobalLock(h);
p = strdup(p);
GlobalUnlock(h);
if(p == nil)
error(Enovmem);
return p;
}
char*
clipread(void)
{
HANDLE h;
char *p;
if(!OpenClipboard(window))
return strdup("");
if((h = GetClipboardData(CF_UNICODETEXT)))
p = clipreadunicode(h);
else if((h = GetClipboardData(CF_TEXT)))
p = clipreadutf(h);
else
p = strdup("");
CloseClipboard();
return p;
}
int
clipwrite(char *buf)
{
HANDLE h;
char *p;
Rune16 *rp;
int n;
n = 0;
if(buf != nil)
n = strlen(buf);
if(!OpenClipboard(window))
return -1;
if(!EmptyClipboard()){
CloseClipboard();
return -1;
}
h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune16));
if(h == NULL)
error(Enovmem);
rp = GlobalLock(h);
utftorunes16(rp, buf, n+1);
GlobalUnlock(h);
SetClipboardData(CF_UNICODETEXT, h);
h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
if(h == NULL)
error(Enovmem);
p = GlobalLock(h);
memmove(p, buf, n);
p[n] = 0;
GlobalUnlock(h);
SetClipboardData(CF_TEXT, h);
CloseClipboard();
return n;
}