ocaml/otherlibs/win32graph/open.c

401 lines
12 KiB
C

/***********************************************************************/
/* */
/* Objective Caml */
/* */
/* Developed by Jacob Navia, based on code by J-M Geffroy and X Leroy */
/* Copyright 2001 Institut National de Recherche en Informatique et */
/* en Automatique. All rights reserved. This file is distributed */
/* under the terms of the GNU Library General Public License, with */
/* the special exception on linking described in file ../../LICENSE. */
/* */
/***********************************************************************/
/* $Id$ */
#include <fcntl.h>
#include <signal.h>
#include "mlvalues.h"
#include "libgraph.h"
#include <windows.h>
static value gr_reset(void);
int MouseLbuttonDown,MouseMbuttonDown,MouseRbuttonDown;
int MouseLastX, MouseLastY;
int LastKey = -1;
static long tid;
static HANDLE threadHandle;
HWND grdisplay = NULL;
int grscreen;
COLORREF grwhite, grblack;
COLORREF grbackground;
int grCurrentColor;
struct canvas grbstore;
BOOL grdisplay_mode;
BOOL grremember_mode;
int grx, gry;
int grcolor;
extern HFONT * grfont;
MSG msg;
HANDLE EventHandle, EventProcessedHandle;
static char *szOcamlWindowClass = "OcamlWindowClass";
static BOOL gr_initialized = 0;
CAMLprim value caml_gr_clear_graph(void);
HANDLE hInst;
HFONT CreationFont(char *name)
{
LOGFONT CurrentFont;
memset(&CurrentFont, 0, sizeof(LOGFONT));
CurrentFont.lfCharSet = ANSI_CHARSET;
CurrentFont.lfWeight = FW_NORMAL;
CurrentFont.lfHeight = grwindow.CurrentFontSize;
CurrentFont.lfPitchAndFamily = (BYTE) (FIXED_PITCH | FF_MODERN);
strcpy(CurrentFont.lfFaceName, name); /* Courier */
return (CreateFontIndirect(&CurrentFont));
}
void SetCoordinates(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd,&rc);
grwindow.width = rc.right;
grwindow.height = rc.bottom;
gr_reset();
}
void ResetForClose(HWND hwnd)
{
DeleteObject(grwindow.hBitmap);
memset(&grwindow,0,sizeof(grwindow));
}
static LRESULT CALLBACK GraphicsWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg) {
// Create the MDI client invisible window
case WM_CREATE:
break;
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
BitBlt(hdc,0,0,grwindow.width,grwindow.height,
grwindow.gcBitmap,0,0,SRCCOPY);
EndPaint(hwnd,&ps);
break;
// Move the child windows
case WM_SIZE:
// Position the MDI client window between the tool and status bars
if (wParam != SIZE_MINIMIZED) {
SetCoordinates(hwnd);
}
return 0;
// End application
case WM_DESTROY:
ResetForClose(hwnd);
break;
case WM_LBUTTONDOWN:
MouseLbuttonDown = 1;
break;
case WM_LBUTTONUP:
MouseLbuttonDown = 0;
break;
case WM_RBUTTONDOWN:
MouseRbuttonDown = 1;
break;
case WM_RBUTTONUP:
MouseRbuttonDown = 0;
break;
case WM_MBUTTONDOWN:
MouseMbuttonDown = 1;
break;
case WM_MBUTTONUP:
MouseMbuttonDown = 0;
break;
case WM_CHAR:
LastKey = wParam & 0xFF;
break;
case WM_KEYUP:
LastKey = -1;
break;
case WM_MOUSEMOVE:
#if 0
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
MapWindowPoints(HWND_DESKTOP,grwindow.hwnd,&pt,1);
MouseLastX = pt.x;
MouseLastY = grwindow.height - 1 - pt.y;
#else
MouseLastX = GET_X_LPARAM(lParam);
MouseLastY = grwindow.height - 1 - GET_Y_LPARAM(lParam);
#endif
break;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
int DoRegisterClass(void)
{
WNDCLASS wc;
memset(&wc,0,sizeof(WNDCLASS));
wc.style = CS_HREDRAW|CS_VREDRAW |CS_DBLCLKS|CS_OWNDC ;
wc.lpfnWndProc = (WNDPROC)GraphicsWndProc;
wc.hInstance = hInst;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = szOcamlWindowClass;
wc.lpszMenuName = 0;
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hIcon = 0;
return RegisterClass(&wc);
}
static value gr_reset(void)
{
RECT rc;
int screenx,screeny;
screenx = GetSystemMetrics(SM_CXSCREEN);
screeny = GetSystemMetrics(SM_CYSCREEN);
GetClientRect(grwindow.hwnd,&rc);
grwindow.gc = GetDC(grwindow.hwnd);
grwindow.width = rc.right;
grwindow.height = rc.bottom;
if (grwindow.gcBitmap == (HDC)0) {
grwindow.hBitmap = CreateCompatibleBitmap(grwindow.gc,screenx,screeny);
grwindow.gcBitmap = CreateCompatibleDC(grwindow.gc);
grwindow.tempDC = CreateCompatibleDC(grwindow.gc);
SelectObject(grwindow.gcBitmap,grwindow.hBitmap);
SetMapMode(grwindow.gcBitmap,MM_TEXT);
MoveToEx(grwindow.gcBitmap,0,grwindow.height-1,0);
BitBlt(grwindow.gcBitmap,0,0,screenx,screeny,
grwindow.gcBitmap,0,0,WHITENESS);
grwindow.CurrentFontSize = 15;
grwindow.CurrentFont = CreationFont("Courier");
}
grwindow.CurrentColor = GetSysColor(COLOR_WINDOWTEXT);
grwindow.grx = 0;
grwindow.gry = 0;
grwindow.CurrentPen = SelectObject(grwindow.gc,GetStockObject(WHITE_PEN));
SelectObject(grwindow.gc,grwindow.CurrentPen);
SelectObject(grwindow.gcBitmap,grwindow.CurrentPen);
grwindow.CurrentBrush = SelectObject(grwindow.gc,GetStockObject(WHITE_BRUSH));
SelectObject(grwindow.gc,grwindow.CurrentBrush);
SelectObject(grwindow.gcBitmap,grwindow.CurrentBrush);
caml_gr_set_color(Val_long(0));
SelectObject(grwindow.gc,grwindow.CurrentFont);
SelectObject(grwindow.gcBitmap,grwindow.CurrentFont);
grdisplay_mode = grremember_mode = 1;
MoveToEx(grwindow.gc,0,grwindow.height-1,0);
MoveToEx(grwindow.gcBitmap,0,grwindow.height-1,0);
SetTextAlign(grwindow.gcBitmap,TA_BOTTOM);
SetTextAlign(grwindow.gc,TA_BOTTOM);
return Val_unit;
}
void SuspendGraphicThread(void)
{
SuspendThread(threadHandle);
}
void ResumeGraphicThread(void)
{
ResumeThread(threadHandle);
}
/* For handshake between the event handling thread and the main thread */
static char * open_graph_errmsg;
static HANDLE open_graph_event;
static DWORD WINAPI gr_open_graph_internal(value arg)
{
RECT rc;
int ret;
int event;
int x, y, w, h;
int screenx,screeny;
int attributes;
static int registered;
MSG msg;
gr_initialized = TRUE;
hInst = GetModuleHandle(NULL);
x = y = w = h = CW_USEDEFAULT;
sscanf(String_val(arg), "%dx%d+%d+%d", &w, &h, &x, &y);
/* Open the display */
if (grwindow.hwnd == NULL || !IsWindow(grwindow.hwnd)) {
if (!registered) {
registered = DoRegisterClass();
if (!registered) {
open_graph_errmsg = "Cannot register the window class";
SetEvent(open_graph_event);
return 1;
}
}
grwindow.hwnd = CreateWindow(szOcamlWindowClass,
WINDOW_NAME,
WS_OVERLAPPEDWINDOW,
x,y,
w,h,
NULL,0,hInst,NULL);
if (grwindow.hwnd == NULL) {
open_graph_errmsg = "Cannot create window";
SetEvent(open_graph_event);
return 1;
}
#if 0
if (x != CW_USEDEFAULT) {
rc.left = 0;
rc.top = 0;
rc.right = w;
rc.bottom = h;
AdjustWindowRect(&rc,GetWindowLong(grwindow.hwnd,GWL_STYLE),0);
MoveWindow(grwindow.hwnd,x,y,rc.right-rc.left,rc.bottom-rc.top,1);
}
#endif
}
gr_reset();
ShowWindow(grwindow.hwnd,SW_SHOWNORMAL);
/* Position the current point at origin */
grwindow.grx = 0;
grwindow.gry = 0;
EventHandle = CreateEvent(NULL,0,0,NULL);
EventProcessedHandle = CreateEvent(NULL,0,0,NULL);
/* The global data structures are now correctly initialized.
Restart the Caml main thread. */
open_graph_errmsg = NULL;
SetEvent(open_graph_event);
/* Enter the message handling loop */
while (GetMessage(&msg,NULL,0,0)) {
if (InspectMessages != NULL) {
*InspectMessages = msg;
SetEvent(EventHandle);
}
TranslateMessage(&msg); // Translates virtual key codes
DispatchMessage(&msg); // Dispatches message to window
if (!IsWindow(grwindow.hwnd))
break;
if (InspectMessages != NULL) {
WaitForSingleObject(EventProcessedHandle,INFINITE);
}
}
return 0;
}
CAMLprim value caml_gr_open_graph(value arg)
{
long tid;
if (gr_initialized) return Val_unit;
open_graph_event = CreateEvent(NULL, FALSE, FALSE, NULL);
threadHandle =
CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)gr_open_graph_internal,(void **)arg,
0,
&tid);
WaitForSingleObject(open_graph_event, INFINITE);
CloseHandle(open_graph_event);
if (open_graph_errmsg != NULL) gr_fail("%s", open_graph_errmsg);
return Val_unit;
}
CAMLprim value caml_gr_close_graph(void)
{
if (gr_initialized) {
DeleteDC(grwindow.tempDC);
DeleteDC(grwindow.gcBitmap);
DestroyWindow(grwindow.hwnd);
memset(&grwindow,0,sizeof(grwindow));
gr_initialized = 0;
}
return Val_unit;
}
CAMLprim value caml_gr_clear_graph(void)
{
gr_check_open();
if(grremember_mode) {
BitBlt(grwindow.gcBitmap,0,0,grwindow.width,grwindow.height,
grwindow.gcBitmap,0,0,WHITENESS);
}
if(grdisplay_mode) {
BitBlt(grwindow.gc,0,0,grwindow.width,grwindow.height,
grwindow.gc,0,0,WHITENESS);
}
return Val_unit;
}
CAMLprim value caml_gr_size_x(void)
{
gr_check_open();
return Val_int(grwindow.width);
}
CAMLprim value caml_gr_size_y(void)
{
gr_check_open();
return Val_int(grwindow.height);
}
CAMLprim value caml_gr_synchronize(void)
{
gr_check_open();
BitBlt(grwindow.gc,0,0,grwindow.width,grwindow.height,
grwindow.gcBitmap,0,0,SRCCOPY);
return Val_unit ;
}
CAMLprim value caml_gr_display_mode(value flag)
{
grdisplay_mode = (Int_val(flag)) ? 1 : 0;
return Val_unit ;
}
CAMLprim value caml_gr_remember_mode(value flag)
{
grremember_mode = (Int_val(flag)) ? 1 : 0;
return Val_unit ;
}
CAMLprim value caml_gr_sigio_signal(value unit)
{
return Val_unit;
}
CAMLprim value caml_gr_sigio_handler(void)
{
return Val_unit;
}
/* Processing of graphic errors */
value * caml_named_value (char * name);
static value * graphic_failure_exn = NULL;
void gr_fail(char *fmt, char *arg)
{
char buffer[1024];
if (graphic_failure_exn == NULL) {
graphic_failure_exn = caml_named_value("Graphics.Graphic_failure");
if (graphic_failure_exn == NULL)
invalid_argument("Exception Graphics.Graphic_failure not initialized, must link graphics.cma");
}
sprintf(buffer, fmt, arg);
raise_with_string(*graphic_failure_exn, buffer);
}
void gr_check_open(void)
{
if (!gr_initialized) gr_fail("graphic screen not opened", NULL);
}