drawterm/gui-win32/screen.c

641 lines
11 KiB
C

#include <windows.h>
#undef Rectangle
#define Rectangle _Rectangle
#include <u.h>
#include <libc.h>
#include <dat.h>
#include <draw.h>
#include <memdraw.h>
#include "error.h"
#include "screen.h"
#include "keyboard.h"
#include "fns.h"
Memimage *gscreen;
Screeninfo screen;
extern int mousequeue;
static int depth;
static HINSTANCE inst;
static HWND window;
static HPALETTE palette;
static LOGPALETTE *logpal;
static Lock gdilock;
static BITMAPINFO *bmi;
static HCURSOR hcursor;
static void winproc(void *);
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
static void paletteinit(void);
static void bmiinit(void);
static void screenload2(Rectangle r, int ldepth, uchar *p, Point pt, int step);
static int readybit;
static Rendez rend;
Point ZP;
static
isready(void*a)
{
return readybit;
}
void
screeninit(void)
{
int fmt;
int dx, dy;
memimageinit();
if(depth == 0)
depth = GetDeviceCaps(GetDC(NULL), BITSPIXEL);
switch(depth){
case 32:
screen.dibtype = DIB_RGB_COLORS;
screen.depth = 32;
fmt = XRGB32;
break;
case 24:
screen.dibtype = DIB_RGB_COLORS;
screen.depth = 24;
fmt = RGB24;
break;
case 16:
screen.dibtype = DIB_RGB_COLORS;
screen.depth = 16;
fmt = RGB15; /* [sic] */
break;
case 8:
default:
screen.dibtype = DIB_PAL_COLORS;
screen.depth = 8;
depth = 8;
fmt = CMAP8;
break;
}
dx = GetDeviceCaps(GetDC(NULL), HORZRES);
dy = GetDeviceCaps(GetDC(NULL), VERTRES);
gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
kproc("winscreen", winproc, 0);
sleep(&rend, isready, 0);
}
uchar*
attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen, void **X)
{
*r = gscreen->r;
*chan = gscreen->chan;
*depth = gscreen->depth;
*width = gscreen->width;
*softscreen = 1;
return gscreen->data->bdata;
}
void
flushmemscreen(Rectangle r)
{
screenload(r, gscreen->depth, byteaddr(gscreen, ZP), ZP,
gscreen->width*sizeof(ulong));
// Sleep(100);
}
void
screenload(Rectangle r, int depth, uchar *p, Point pt, int step)
{
int dx, dy, delx;
HDC hdc;
RECT winr;
if(depth != gscreen->depth)
panic("screenload: bad ldepth");
/*
* Sometimes we do get rectangles that are off the
* screen to the negative axes, for example, when
* dragging around a window border in a Move operation.
*/
if(rectclip(&r, gscreen->r) == 0)
return;
if(step&3 != 0 || ((pt.x*depth)%32) != 0 || (ulong)p&3 != 0)
panic("screenload: bad params %d %d %ux", step, pt.x, p);
dx = r.max.x - r.min.x;
dy = r.max.y - r.min.y;
if(dx <= 0 || dy <= 0)
return;
if(depth == 24)
delx = r.min.x % 4;
else
delx = r.min.x & (31/depth);
p += (r.min.y-pt.y)*step;
p += ((r.min.x-delx-pt.x)*depth)>>3;
if(GetWindowRect(window, &winr)==0)
return;
if(rectclip(&r, Rect(0, 0, winr.right-winr.left, winr.bottom-winr.top))==0)
return;
lock(&gdilock);
hdc = GetDC(window);
SelectPalette(hdc, palette, 0);
RealizePalette(hdc);
//FillRect(hdc,(void*)&r, GetStockObject(BLACK_BRUSH));
//GdiFlush();
//Sleep(100);
bmi->bmiHeader.biWidth = (step*8)/depth;
bmi->bmiHeader.biHeight = -dy; /* - => origin upper left */
StretchDIBits(hdc, r.min.x, r.min.y, dx, dy,
delx, 0, dx, dy, p, bmi, screen.dibtype, SRCCOPY);
ReleaseDC(window, hdc);
GdiFlush();
unlock(&gdilock);
}
static void
winproc(void *a)
{
WNDCLASS wc;
MSG msg;
inst = GetModuleHandle(NULL);
paletteinit();
bmiinit();
terminit();
wc.style = 0;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = inst;
wc.hIcon = LoadIcon(inst, NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "9pmgraphics";
RegisterClass(&wc);
window = CreateWindowEx(
0, /* extended style */
"9pmgraphics", /* class */
"drawterm screen", /* caption */
WS_OVERLAPPEDWINDOW, /* style */
CW_USEDEFAULT, /* init. x pos */
CW_USEDEFAULT, /* init. y pos */
CW_USEDEFAULT, /* init. x size */
CW_USEDEFAULT, /* init. y size */
NULL, /* parent window (actually owner window for overlapped)*/
NULL, /* menu handle */
inst, /* program handle */
NULL /* create parms */
);
if(window == nil)
panic("can't make window\n");
ShowWindow(window, SW_SHOWDEFAULT);
UpdateWindow(window);
readybit = 1;
wakeup(&rend);
screen.reshaped = 0;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// MessageBox(0, "winproc", "exits", MB_OK);
ExitProcess(0);
}
int
col(int v, int n)
{
int i, c;
c = 0;
for(i = 0; i < 8; i += n)
c |= v << (16-(n+i));
return c >> 8;
}
void
paletteinit(void)
{
PALETTEENTRY *pal;
int r, g, b, cr, cg, cb, v;
int num, den;
int i, j;
logpal = mallocz(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY), 1);
if(logpal == nil)
panic("out of memory");
logpal->palVersion = 0x300;
logpal->palNumEntries = 256;
pal = logpal->palPalEntry;
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;
/* divide check -- pick grey shades */
if(den==0)
cr=cg=cb=v*17;
else{
num=17*(4*den+v);
cr=r*num/den;
cg=g*num/den;
cb=b*num/den;
}
pal[i+(j&15)].peRed = cr;
pal[i+(j&15)].peGreen = cg;
pal[i+(j&15)].peBlue = cb;
pal[i+(j&15)].peFlags = 0;
}
}
}
}
palette = CreatePalette(logpal);
}
void
getcolor(ulong i, ulong *r, ulong *g, ulong *b)
{
PALETTEENTRY *pal;
pal = logpal->palPalEntry;
*r = pal[i].peRed;
*g = pal[i].peGreen;
*b = pal[i].peBlue;
}
void
bmiinit(void)
{
ushort *p;
int i;
bmi = mallocz(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD), 1);
if(bmi == 0)
panic("out of memory");
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi->bmiHeader.biWidth = 0;
bmi->bmiHeader.biHeight = 0; /* - => origin upper left */
bmi->bmiHeader.biPlanes = 1;
bmi->bmiHeader.biBitCount = depth;
bmi->bmiHeader.biCompression = BI_RGB;
bmi->bmiHeader.biSizeImage = 0;
bmi->bmiHeader.biXPelsPerMeter = 0;
bmi->bmiHeader.biYPelsPerMeter = 0;
bmi->bmiHeader.biClrUsed = 0;
bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */
p = (ushort*)bmi->bmiColors;
for(i = 0; i < 256; i++)
p[i] = i;
}
LRESULT CALLBACK
WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT paint;
HDC hdc;
LONG x, y, b;
int i;
Rectangle r;
switch(msg) {
case WM_CREATE:
break;
case WM_SETCURSOR:
/* User set */
if(hcursor != NULL) {
SetCursor(hcursor);
return 1;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
x = LOWORD(lparam);
y = HIWORD(lparam);
b = 0;
if(wparam & MK_LBUTTON)
b = 1;
if(wparam & MK_MBUTTON)
b |= 2;
if(wparam & MK_RBUTTON) {
if(wparam & MK_SHIFT)
b |= 2;
else
b |= 4;
}
lock(&mouse.lk);
i = mouse.wi;
if(mousequeue) {
if(i == mouse.ri || mouse.lastb != b || mouse.trans) {
mouse.wi = (i+1)%Mousequeue;
if(mouse.wi == mouse.ri)
mouse.ri = (mouse.ri+1)%Mousequeue;
mouse.trans = mouse.lastb != b;
} else {
i = (i-1+Mousequeue)%Mousequeue;
}
} else {
mouse.wi = (i+1)%Mousequeue;
mouse.ri = i;
}
mouse.queue[i].xy.x = x;
mouse.queue[i].xy.y = y;
mouse.queue[i].buttons = b;
mouse.queue[i].msec = ticks();
mouse.lastb = b;
unlock(&mouse.lk);
wakeup(&mouse.r);
break;
case WM_CHAR:
/* repeat count is lparam & 0xf */
switch(wparam){
case '\n':
wparam = '\r';
break;
case '\r':
wparam = '\n';
break;
}
kbdputc(kbdq, wparam);
break;
case WM_SYSKEYUP:
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
switch(wparam) {
case VK_MENU:
kbdputc(kbdq, Kalt);
break;
case VK_INSERT:
kbdputc(kbdq, Kins);
break;
case VK_DELETE:
// kbdputc(kbdq, Kdel);
kbdputc(kbdq, 0x7f); // should have Kdel in keyboard.h
break;
case VK_UP:
kbdputc(kbdq, Kup);
break;
case VK_DOWN:
kbdputc(kbdq, Kdown);
break;
case VK_LEFT:
kbdputc(kbdq, Kleft);
break;
case VK_RIGHT:
kbdputc(kbdq, Kright);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(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);
r.min.x = paint.rcPaint.left;
r.min.y = paint.rcPaint.top;
r.max.x = paint.rcPaint.right;
r.max.y = paint.rcPaint.bottom;
flushmemscreen(r);
EndPaint(hwnd, &paint);
break;
case WM_COMMAND:
case WM_SETFOCUS:
case WM_DEVMODECHANGE:
case WM_WININICHANGE:
case WM_INITMENU:
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}
void
mouseset(Point xy)
{
POINT pt;
pt.x = xy.x;
pt.y = xy.y;
MapWindowPoints(window, 0, &pt, 1);
SetCursorPos(pt.x, pt.y);
}
void
setcursor(void)
{
HCURSOR nh;
int x, y, h, w;
uchar *sp, *cp;
uchar *and, *xor;
h = GetSystemMetrics(SM_CYCURSOR);
w = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
and = mallocz(h*w, 1);
memset(and, 0xff, h*w);
xor = mallocz(h*w, 1);
lock(&cursor.lk);
for(y=0,sp=cursor.set,cp=cursor.clr; y<16; y++) {
for(x=0; x<2; x++) {
and[y*w+x] = ~(*sp|*cp);
xor[y*w+x] = ~*sp & *cp;
cp++;
sp++;
}
}
nh = CreateCursor(inst, -cursor.offset.x, -cursor.offset.y,
GetSystemMetrics(SM_CXCURSOR), h,
and, xor);
if(nh != NULL) {
SetCursor(nh);
if(hcursor != NULL)
DestroyCursor(hcursor);
hcursor = nh;
}
unlock(&cursor.lk);
free(and);
free(xor);
PostMessage(window, WM_SETCURSOR, (int)window, 0);
}
void
cursorarrow(void)
{
if(hcursor != 0) {
DestroyCursor(hcursor);
hcursor = 0;
}
SetCursor(LoadCursor(0, IDC_ARROW));
PostMessage(window, WM_SETCURSOR, (int)window, 0);
}
void
setcolor(ulong index, ulong red, ulong green, ulong blue)
{
}
uchar*
clipreadunicode(HANDLE h)
{
Rune *p;
int n;
uchar *q;
p = GlobalLock(h);
n = wstrutflen(p)+1;
q = malloc(n);
wstrtoutf(q, p, n);
GlobalUnlock(h);
return q;
}
uchar *
clipreadutf(HANDLE h)
{
uchar *p;
p = GlobalLock(h);
p = strdup(p);
GlobalUnlock(h);
return p;
}
char*
clipread(void)
{
HANDLE h;
uchar *p;
if(!OpenClipboard(window)) {
oserror();
return strdup("");
}
if(h = GetClipboardData(CF_UNICODETEXT))
p = clipreadunicode(h);
else if(h = GetClipboardData(CF_TEXT))
p = clipreadutf(h);
else {
oserror();
p = strdup("");
}
CloseClipboard();
return p;
}
int
clipwrite(char *buf)
{
HANDLE h;
char *p, *e;
Rune *rp;
int n = strlen(buf);
if(!OpenClipboard(window)) {
oserror();
return -1;
}
if(!EmptyClipboard()) {
oserror();
CloseClipboard();
return -1;
}
h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune));
if(h == NULL)
panic("out of memory");
rp = GlobalLock(h);
p = buf;
e = p+n;
while(p<e)
p += chartorune(rp++, p);
*rp = 0;
GlobalUnlock(h);
SetClipboardData(CF_UNICODETEXT, h);
h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
if(h == NULL)
panic("out of memory");
p = GlobalLock(h);
memcpy(p, buf, n);
p[n] = 0;
GlobalUnlock(h);
SetClipboardData(CF_TEXT, h);
CloseClipboard();
return n;
}
int
atlocalconsole(void)
{
return 1;
}