1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/*
25	GEM video driver
26
27	Patrice Mandin
28	and work from
29	Olivier Landemarre, Johan Klockars, Xavier Joubert, Claude Attard
30*/
31
32/* Mint includes */
33#include <gem.h>
34#include <gemx.h>
35#include <mint/osbind.h>
36#include <mint/cookie.h>
37
38#include "SDL_endian.h"
39#include "SDL_video.h"
40#include "SDL_mouse.h"
41#include "../SDL_sysvideo.h"
42#include "../SDL_pixels_c.h"
43#include "../../events/SDL_events_c.h"
44#include "../SDL_cursor_c.h"
45
46#include "../ataricommon/SDL_ataric2p_s.h"
47#include "../ataricommon/SDL_atarieddi_s.h"
48#include "../ataricommon/SDL_atarimxalloc_c.h"
49#include "../ataricommon/SDL_atarigl_c.h"
50
51#include "SDL_gemvideo.h"
52#include "SDL_gemevents_c.h"
53#include "SDL_gemmouse_c.h"
54#include "SDL_gemwm_c.h"
55#include "../ataricommon/SDL_xbiosevents_c.h"
56#include "../ataricommon/SDL_ataridevmouse_c.h"
57
58/* Defines */
59
60/*#define DEBUG_VIDEO_GEM	1*/
61
62#define GEM_VID_DRIVER_NAME "gem"
63
64#undef MIN
65#define MIN(a,b) (((a)<(b)) ? (a) : (b))
66#undef MAX
67#define MAX(a,b) (((a)>(b)) ? (a) : (b))
68
69/* Variables */
70
71static unsigned char vdi_index[256] = {
72	0,  2,  3,  6,  4,  7,  5,   8,
73	9, 10, 11, 14, 12, 15, 13, 255
74};
75
76static const unsigned char empty_name[]="";
77
78/* Initialization/Query functions */
79static int GEM_VideoInit(_THIS, SDL_PixelFormat *vformat);
80static SDL_Rect **GEM_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
81static SDL_Surface *GEM_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
82static int GEM_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
83static void GEM_VideoQuit(_THIS);
84
85/* Hardware surface functions */
86static int GEM_AllocHWSurface(_THIS, SDL_Surface *surface);
87static int GEM_LockHWSurface(_THIS, SDL_Surface *surface);
88static int GEM_FlipHWSurface(_THIS, SDL_Surface *surface);
89static void GEM_UnlockHWSurface(_THIS, SDL_Surface *surface);
90static void GEM_FreeHWSurface(_THIS, SDL_Surface *surface);
91static void GEM_UpdateRects(_THIS, int numrects, SDL_Rect *rects);
92#if 0
93static int GEM_ToggleFullScreen(_THIS, int on);
94#endif
95
96/* Internal functions */
97static void GEM_FreeBuffers(_THIS);
98static void GEM_ClearScreen(_THIS);
99static void GEM_ClearRect(_THIS, short *rect);
100static void GEM_SetNewPalette(_THIS, Uint16 newpal[256][3]);
101static void GEM_LockScreen(_THIS);
102static void GEM_UnlockScreen(_THIS);
103static void refresh_window(_THIS, int winhandle, short *rect);
104
105#if SDL_VIDEO_OPENGL
106/* OpenGL functions */
107static void GEM_GL_SwapBuffers(_THIS);
108#endif
109
110/* GEM driver bootstrap functions */
111
112static int GEM_Available(void)
113{
114	/* Test if AES available */
115	if (appl_init() == -1)
116		return 0;
117
118	appl_exit();
119	return 1;
120}
121
122static void GEM_DeleteDevice(SDL_VideoDevice *device)
123{
124	SDL_free(device->hidden);
125	SDL_free(device);
126}
127
128static SDL_VideoDevice *GEM_CreateDevice(int devindex)
129{
130	SDL_VideoDevice *device;
131	int vectors_mask;
132	unsigned long dummy;
133
134	/* Initialize all variables that we clean on shutdown */
135	device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
136	if ( device ) {
137		SDL_memset(device, 0, (sizeof *device));
138		device->hidden = (struct SDL_PrivateVideoData *)
139				SDL_malloc((sizeof *device->hidden));
140		device->gl_data = (struct SDL_PrivateGLData *)
141				SDL_malloc((sizeof *device->gl_data));
142	}
143	if ( (device == NULL) || (device->hidden == NULL) ) {
144		SDL_OutOfMemory();
145		if ( device ) {
146			SDL_free(device);
147		}
148		return(0);
149	}
150	SDL_memset(device->hidden, 0, (sizeof *device->hidden));
151	SDL_memset(device->gl_data, 0, sizeof(*device->gl_data));
152
153	/* Set the function pointers */
154	device->VideoInit = GEM_VideoInit;
155	device->ListModes = GEM_ListModes;
156	device->SetVideoMode = GEM_SetVideoMode;
157	device->SetColors = GEM_SetColors;
158	device->UpdateRects = NULL /*GEM_UpdateRects*/;
159	device->VideoQuit = GEM_VideoQuit;
160	device->AllocHWSurface = GEM_AllocHWSurface;
161	device->LockHWSurface = GEM_LockHWSurface;
162	device->UnlockHWSurface = GEM_UnlockHWSurface;
163	device->FlipHWSurface = GEM_FlipHWSurface;
164	device->FreeHWSurface = GEM_FreeHWSurface;
165	device->ToggleFullScreen = NULL /*GEM_ToggleFullScreen*/;
166
167	/* Window manager */
168	device->SetCaption = GEM_SetCaption;
169	device->SetIcon = GEM_SetIcon;
170	device->IconifyWindow = GEM_IconifyWindow;
171	device->GrabInput = GEM_GrabInput;
172
173	/* Events */
174	device->InitOSKeymap = GEM_InitOSKeymap;
175	device->PumpEvents = GEM_PumpEvents;
176
177	/* Mouse */
178	device->FreeWMCursor = GEM_FreeWMCursor;
179	device->CreateWMCursor = GEM_CreateWMCursor;
180	device->ShowWMCursor = GEM_ShowWMCursor;
181	device->WarpWMCursor = NULL /*GEM_WarpWMCursor*/;
182	device->CheckMouseMode = GEM_CheckMouseMode;
183
184#if SDL_VIDEO_OPENGL
185	/* OpenGL functions */
186	device->GL_LoadLibrary = SDL_AtariGL_LoadLibrary;
187	device->GL_GetProcAddress = SDL_AtariGL_GetProcAddress;
188	device->GL_GetAttribute = SDL_AtariGL_GetAttribute;
189	device->GL_MakeCurrent = SDL_AtariGL_MakeCurrent;
190	device->GL_SwapBuffers = GEM_GL_SwapBuffers;
191#endif
192
193	device->hidden->use_dev_mouse =
194		(SDL_AtariDevMouse_Open()!=0) ? SDL_TRUE : SDL_FALSE;
195
196	vectors_mask = ATARI_XBIOS_JOYSTICKEVENTS;	/* XBIOS joystick events */
197	if (!(device->hidden->use_dev_mouse)) {
198		vectors_mask |= ATARI_XBIOS_MOUSEEVENTS;	/* XBIOS mouse events */
199	}
200/*	if (Getcookie(C_MiNT, &dummy)==C_FOUND) {
201		vectors_mask = 0;
202	}*/
203
204	SDL_AtariXbios_InstallVectors(vectors_mask);
205
206	device->free = GEM_DeleteDevice;
207
208	return device;
209}
210
211VideoBootStrap GEM_bootstrap = {
212	GEM_VID_DRIVER_NAME, "Atari GEM video driver",
213	GEM_Available, GEM_CreateDevice
214};
215
216static void VDI_ReadExtInfo(_THIS, short *work_out)
217{
218	unsigned long EdDI_version;
219	unsigned long cookie_EdDI;
220	Uint32 num_colours;
221	Uint16 clut_type, num_bits;
222
223	/* Read EdDI informations */
224	if  (Getcookie(C_EdDI, &cookie_EdDI) == C_NOTFOUND) {
225		return;
226	}
227
228	EdDI_version = Atari_get_EdDI_version( (void *)cookie_EdDI);
229
230	vq_scrninfo(VDI_handle, work_out);
231
232	VDI_format = work_out[0];
233	clut_type = work_out[1];
234	num_bits = work_out[2];
235	num_colours = *((Uint32 *) &work_out[3]);
236
237	/* With EdDI>=1.1, we can have screen pitch, address and format
238	 * so we can directly write to screen without using vro_cpyfm
239	 */
240	if (EdDI_version >= EDDI_11) {
241		VDI_pitch = work_out[5];
242		VDI_screen = (void *) *((unsigned long *) &work_out[6]);
243	}
244
245	switch(clut_type) {
246		case VDI_CLUT_HARDWARE:
247			{
248				int i;
249				Uint16 *tmp_p;
250
251				tmp_p = (Uint16 *)&work_out[16];
252
253				for (i=0;i<256;i++) {
254					vdi_index[*tmp_p++] = i;
255				}
256			}
257			break;
258		case VDI_CLUT_SOFTWARE:
259			{
260				int component; /* red, green, blue, alpha, overlay */
261				int num_bit;
262				unsigned short *tmp_p;
263
264				/* We can build masks with info here */
265				tmp_p = (unsigned short *) &work_out[16];
266				for (component=0;component<5;component++) {
267					for (num_bit=0;num_bit<16;num_bit++) {
268						unsigned short valeur;
269
270						valeur = *tmp_p++;
271
272						if (valeur == 0xffff) {
273							continue;
274						}
275
276						switch(component) {
277							case 0:
278								VDI_redmask |= 1<< valeur;
279								break;
280							case 1:
281								VDI_greenmask |= 1<< valeur;
282								break;
283							case 2:
284								VDI_bluemask |= 1<< valeur;
285								break;
286							case 3:
287								VDI_alphamask |= 1<< valeur;
288								break;
289						}
290					}
291				}
292			}
293
294			/* Remove lower green bits for Intel endian screen */
295			if ((VDI_greenmask == ((7<<13)|3)) || (VDI_greenmask == ((7<<13)|7))) {
296				VDI_greenmask &= ~(7<<13);
297			}
298			break;
299		case VDI_CLUT_NONE:
300			break;
301	}
302}
303
304int GEM_VideoInit(_THIS, SDL_PixelFormat *vformat)
305{
306	int i, menubar_size;
307	short work_in[12], work_out[272], dummy;
308
309	/* Open AES (Application Environment Services) */
310	if (appl_init() == -1) {
311		fprintf(stderr,"Can not open AES\n");
312		return 1;
313	}
314
315	/* Read version and features */
316	GEM_version = aes_global[0];
317	if (GEM_version >= 0x0410) {
318		short ap_gout[4], errorcode;
319
320		GEM_wfeatures=0;
321		errorcode=appl_getinfo(AES_WINDOW, &ap_gout[0], &ap_gout[1], &ap_gout[2], &ap_gout[3]);
322
323		if (errorcode==0) {
324			GEM_wfeatures=ap_gout[0];
325		}
326	}
327
328	/* Ask VDI physical workstation handle opened by AES */
329	VDI_handle = graf_handle(&dummy, &dummy, &dummy, &dummy);
330	if (VDI_handle<1) {
331		fprintf(stderr,"Wrong VDI handle %d returned by AES\n",VDI_handle);
332		return 1;
333	}
334
335	/* Open virtual VDI workstation */
336	work_in[0]=Getrez()+2;
337	for(i = 1; i < 10; i++)
338		work_in[i] = 1;
339	work_in[10] = 2;
340
341	v_opnvwk(work_in, &VDI_handle, work_out);
342	if (VDI_handle == 0) {
343		fprintf(stderr,"Can not open VDI virtual workstation\n");
344		return 1;
345	}
346
347	/* Read fullscreen size */
348	VDI_w = work_out[0] + 1;
349	VDI_h = work_out[1] + 1;
350
351	/* Read desktop size and position */
352	if (!wind_get(DESKTOP_HANDLE, WF_WORKXYWH, &GEM_desk_x, &GEM_desk_y, &GEM_desk_w, &GEM_desk_h)) {
353		fprintf(stderr,"Can not read desktop properties\n");
354		return 1;
355	}
356
357	/* Read bit depth */
358	vq_extnd(VDI_handle, 1, work_out);
359	VDI_bpp = work_out[4];
360	VDI_oldnumcolors=0;
361
362	switch(VDI_bpp) {
363		case 8:
364			VDI_pixelsize=1;
365			break;
366		case 15:
367		case 16:
368			VDI_pixelsize=2;
369			break;
370		case 24:
371			VDI_pixelsize=3;
372			break;
373		case 32:
374			VDI_pixelsize=4;
375			break;
376		default:
377			fprintf(stderr,"%d bits colour depth not supported\n",VDI_bpp);
378			return 1;
379	}
380
381	/* Setup hardware -> VDI palette mapping */
382	for(i = 16; i < 255; i++) {
383		vdi_index[i] = i;
384	}
385	vdi_index[255] = 1;
386
387	/* Save current palette */
388	if (VDI_bpp>8) {
389		VDI_oldnumcolors=1<<8;
390	} else {
391		VDI_oldnumcolors=1<<VDI_bpp;
392	}
393
394	for(i = 0; i < VDI_oldnumcolors; i++) {
395		short rgb[3];
396
397		vq_color(VDI_handle, i, 0, rgb);
398
399		VDI_oldpalette[i][0] = rgb[0];
400		VDI_oldpalette[i][1] = rgb[1];
401		VDI_oldpalette[i][2] = rgb[2];
402	}
403	VDI_setpalette = GEM_SetNewPalette;
404	SDL_memcpy(VDI_curpalette,VDI_oldpalette,sizeof(VDI_curpalette));
405
406	/* Setup screen info */
407	GEM_title_name = empty_name;
408	GEM_icon_name = empty_name;
409
410	GEM_handle = -1;
411	GEM_locked = SDL_FALSE;
412	GEM_win_fulled = SDL_FALSE;
413	GEM_fullscreen = SDL_FALSE;
414	GEM_lock_redraw = SDL_TRUE;	/* Prevent redraw till buffers are setup */
415
416	VDI_screen = NULL;
417	VDI_pitch = VDI_w * VDI_pixelsize;
418	VDI_format = ( (VDI_bpp <= 8) ? VDI_FORMAT_INTER : VDI_FORMAT_PACK);
419	VDI_redmask = VDI_greenmask = VDI_bluemask = VDI_alphamask = 0;
420	VDI_ReadExtInfo(this, work_out);
421
422#ifdef DEBUG_VIDEO_GEM
423	printf("sdl:video:gem: screen: address=0x%08x, pitch=%d\n", VDI_screen, VDI_pitch);
424	printf("sdl:video:gem: format=%d\n", VDI_format);
425	printf("sdl:video:gem: masks: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
426		VDI_alphamask, VDI_redmask, VDI_greenmask, VDI_bluemask
427	);
428#endif
429
430	/* Setup destination mfdb */
431	VDI_dst_mfdb.fd_addr = NULL;
432
433	/* Determine the current screen size */
434	this->info.current_w = VDI_w;
435	this->info.current_h = VDI_h;
436
437	/* Determine the screen depth */
438	/* we change this during the SDL_SetVideoMode implementation... */
439	vformat->BitsPerPixel = VDI_bpp;
440
441	/* Set mouse cursor to arrow */
442	graf_mouse(ARROW, NULL);
443	GEM_cursor = NULL;
444
445	/* Init chunky to planar routine */
446	SDL_Atari_C2pConvert = SDL_Atari_C2pConvert8;
447
448	/* Setup VDI fill functions */
449	vsf_color(VDI_handle,0);
450	vsf_interior(VDI_handle,1);
451	vsf_perimeter(VDI_handle,0);
452
453	/* Menu bar save buffer */
454	menubar_size = GEM_desk_w * GEM_desk_y * VDI_pixelsize;
455	GEM_menubar=Atari_SysMalloc(menubar_size,MX_PREFTTRAM);
456
457	/* Fill video modes list */
458	SDL_modelist[0] = SDL_malloc(sizeof(SDL_Rect));
459	SDL_modelist[0]->x = 0;
460	SDL_modelist[0]->y = 0;
461	SDL_modelist[0]->w = VDI_w;
462	SDL_modelist[0]->h = VDI_h;
463
464	SDL_modelist[1] = NULL;
465
466#if SDL_VIDEO_OPENGL
467	SDL_AtariGL_InitPointers(this);
468#endif
469
470	this->info.wm_available = 1;
471
472	/* We're done! */
473	return(0);
474}
475
476SDL_Rect **GEM_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
477{
478	if (format->BitsPerPixel != VDI_bpp) {
479		return ((SDL_Rect **)NULL);
480	}
481
482	if (flags & SDL_FULLSCREEN) {
483		return (SDL_modelist);
484	}
485
486	return((SDL_Rect **)-1);
487}
488
489static void GEM_FreeBuffers(_THIS)
490{
491	/* Release buffer */
492	if ( GEM_buffer2 ) {
493		Mfree( GEM_buffer2 );
494		GEM_buffer2=NULL;
495	}
496
497	if ( GEM_buffer1 ) {
498		Mfree( GEM_buffer1 );
499		GEM_buffer1=NULL;
500	}
501}
502
503static void GEM_ClearRect(_THIS, short *rect)
504{
505	short oldrgb[3], rgb[3]={0,0,0};
506
507	vq_color(VDI_handle, vdi_index[0], 0, oldrgb);
508	vs_color(VDI_handle, vdi_index[0], rgb);
509
510	vsf_color(VDI_handle,0);
511	vsf_interior(VDI_handle,1);
512	vsf_perimeter(VDI_handle,0);
513	v_bar(VDI_handle, rect);
514
515	vs_color(VDI_handle, vdi_index[0], oldrgb);
516}
517
518static void GEM_ClearScreen(_THIS)
519{
520	short pxy[4];
521
522	v_hide_c(VDI_handle);
523
524	pxy[0] = pxy[1] = 0;
525	pxy[2] = VDI_w - 1;
526	pxy[3] = VDI_h - 1;
527	GEM_ClearRect(this, pxy);
528
529	v_show_c(VDI_handle, 1);
530}
531
532static void GEM_SetNewPalette(_THIS, Uint16 newpal[256][3])
533{
534	int i;
535	short rgb[3];
536
537	if (VDI_oldnumcolors==0)
538		return;
539
540	for(i = 0; i < VDI_oldnumcolors; i++) {
541		rgb[0] = newpal[i][0];
542		rgb[1] = newpal[i][1];
543		rgb[2] = newpal[i][2];
544
545		vs_color(VDI_handle, i, rgb);
546	}
547}
548
549static void GEM_LockScreen(_THIS)
550{
551	if (!GEM_locked) {
552		/* Lock AES */
553		wind_update(BEG_UPDATE);
554		wind_update(BEG_MCTRL);
555		/* Reserve memory space, used to be sure of compatibility */
556		form_dial( FMD_START, 0,0,0,0, 0,0,VDI_w,VDI_h);
557
558		/* Save menu bar */
559		if (GEM_menubar) {
560			MFDB mfdb_src;
561			short blitcoords[8];
562
563			mfdb_src.fd_addr=GEM_menubar;
564			mfdb_src.fd_w=GEM_desk_w;
565			mfdb_src.fd_h=GEM_desk_y;
566			mfdb_src.fd_wdwidth=GEM_desk_w>>4;
567			mfdb_src.fd_nplanes=VDI_bpp;
568			mfdb_src.fd_stand=
569				mfdb_src.fd_r1=
570				mfdb_src.fd_r2=
571				mfdb_src.fd_r3= 0;
572
573			blitcoords[0] = blitcoords[4] = 0;
574			blitcoords[1] = blitcoords[5] = 0;
575			blitcoords[2] = blitcoords[6] = GEM_desk_w-1;
576			blitcoords[3] = blitcoords[7] = GEM_desk_y-1;
577
578			vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &VDI_dst_mfdb, &mfdb_src);
579		}
580
581		GEM_locked=SDL_TRUE;
582	}
583}
584
585static void GEM_UnlockScreen(_THIS)
586{
587	if (GEM_locked) {
588		/* Restore menu bar */
589		if (GEM_menubar) {
590			MFDB mfdb_src;
591			short blitcoords[8];
592
593			mfdb_src.fd_addr=GEM_menubar;
594			mfdb_src.fd_w=GEM_desk_w;
595			mfdb_src.fd_h=GEM_desk_y;
596			mfdb_src.fd_wdwidth=GEM_desk_w>>4;
597			mfdb_src.fd_nplanes=VDI_bpp;
598			mfdb_src.fd_stand=
599				mfdb_src.fd_r1=
600				mfdb_src.fd_r2=
601				mfdb_src.fd_r3= 0;
602
603			blitcoords[0] = blitcoords[4] = 0;
604			blitcoords[1] = blitcoords[5] = 0;
605			blitcoords[2] = blitcoords[6] = GEM_desk_w-1;
606			blitcoords[3] = blitcoords[7] = GEM_desk_y-1;
607
608			vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src, &VDI_dst_mfdb);
609		}
610
611		/* Restore screen memory, and send REDRAW to all apps */
612		form_dial( FMD_FINISH, 0,0,0,0, 0,0,VDI_w,VDI_h);
613		/* Unlock AES */
614		wind_update(END_MCTRL);
615		wind_update(END_UPDATE);
616
617		GEM_locked=SDL_FALSE;
618	}
619}
620
621SDL_Surface *GEM_SetVideoMode(_THIS, SDL_Surface *current,
622				int width, int height, int bpp, Uint32 flags)
623{
624	Uint32 modeflags, screensize;
625	SDL_bool use_shadow1, use_shadow2;
626
627	/* width must be multiple of 16, for vro_cpyfm() and c2p_convert() */
628	if ((width & 15) != 0) {
629		width = (width | 15) +1;
630	}
631
632	/*--- Verify if asked mode can be used ---*/
633	if (VDI_bpp != bpp) {
634		SDL_SetError("%d bpp mode not supported", bpp);
635		return(NULL);
636	}
637
638	if (flags & SDL_FULLSCREEN) {
639		if ((VDI_w < width) || (VDI_h < height)) {
640			SDL_SetError("%dx%d mode is too large", width, height);
641			return(NULL);
642		}
643	}
644
645	/*--- Allocate the new pixel format for the screen ---*/
646	if ( ! SDL_ReallocFormat(current, VDI_bpp, VDI_redmask, VDI_greenmask, VDI_bluemask, VDI_alphamask) ) {
647		SDL_SetError("Couldn't allocate new pixel format for requested mode");
648		return(NULL);
649	}
650
651	screensize = width * height * VDI_pixelsize;
652
653#ifdef DEBUG_VIDEO_GEM
654	printf("sdl:video:gem: setvideomode(): %dx%dx%d = %d\n", width, height, bpp, screensize);
655#endif
656
657	/*--- Allocate shadow buffers if needed, and conversion operations ---*/
658	GEM_FreeBuffers(this);
659
660	GEM_bufops=0;
661	use_shadow1=use_shadow2=SDL_FALSE;
662	if (VDI_screen && (flags & SDL_FULLSCREEN)) {
663		if (VDI_format==VDI_FORMAT_INTER) {
664			use_shadow1=SDL_TRUE;
665			GEM_bufops = B2S_C2P_1TOS;
666		}
667	} else {
668		use_shadow1=SDL_TRUE;
669		if (VDI_format==VDI_FORMAT_PACK) {
670			GEM_bufops = B2S_VROCPYFM_1TOS;
671		} else {
672			use_shadow2=SDL_TRUE;
673			GEM_bufops = B2S_C2P_1TO2|B2S_VROCPYFM_2TOS;
674		}
675	}
676
677	if (use_shadow1) {
678		GEM_buffer1 = Atari_SysMalloc(screensize, MX_PREFTTRAM);
679		if (GEM_buffer1==NULL) {
680			SDL_SetError("Can not allocate %d KB for frame buffer", screensize>>10);
681			return NULL;
682		}
683		SDL_memset(GEM_buffer1, 0, screensize);
684#ifdef DEBUG_VIDEO_GEM
685		printf("sdl:video:gem: setvideomode(): allocated buffer 1\n");
686#endif
687	}
688
689	if (use_shadow2) {
690		GEM_buffer2 = Atari_SysMalloc(screensize, MX_PREFTTRAM);
691		if (GEM_buffer2==NULL) {
692			SDL_SetError("Can not allocate %d KB for shadow buffer", screensize>>10);
693			return NULL;
694		}
695		SDL_memset(GEM_buffer2, 0, screensize);
696#ifdef DEBUG_VIDEO_GEM
697		printf("sdl:video:gem: setvideomode(): allocated buffer 2\n");
698#endif
699	}
700
701	/*--- Initialize screen ---*/
702	modeflags = SDL_PREALLOC;
703	if (VDI_bpp == 8) {
704		modeflags |= SDL_HWPALETTE;
705	}
706
707	if (flags & SDL_FULLSCREEN) {
708		GEM_LockScreen(this);
709
710		GEM_ClearScreen(this);
711
712		modeflags |= SDL_FULLSCREEN;
713		if (VDI_screen && (VDI_format==VDI_FORMAT_PACK) && !use_shadow1) {
714			modeflags |= SDL_HWSURFACE;
715		} else {
716			modeflags |= SDL_SWSURFACE;
717		}
718
719		GEM_fullscreen = SDL_TRUE;
720	} else {
721		int old_win_type;
722		short x2,y2,w2,h2;
723
724		GEM_UnlockScreen(this);
725
726		/* Set window gadgets */
727		old_win_type = GEM_win_type;
728		if (!(flags & SDL_NOFRAME)) {
729			GEM_win_type=NAME|MOVER|CLOSER|SMALLER;
730			if (flags & SDL_RESIZABLE) {
731				GEM_win_type |= FULLER|SIZER;
732				modeflags |= SDL_RESIZABLE;
733			}
734		} else {
735			GEM_win_type=0;
736			modeflags |= SDL_NOFRAME;
737		}
738		modeflags |= SDL_SWSURFACE;
739
740		/* Recreate window ? only for different widget or non-created window */
741		if ((old_win_type != GEM_win_type) || (GEM_handle < 0)) {
742			/* Calculate window size */
743			if (!wind_calc(WC_BORDER, GEM_win_type, 0,0,width,height, &x2,&y2,&w2,&h2)) {
744				GEM_FreeBuffers(this);
745				SDL_SetError("Can not calculate window attributes");
746				return NULL;
747			}
748
749			/* Center window */
750			x2 = (GEM_desk_w-w2)>>1;
751			y2 = (GEM_desk_h-h2)>>1;
752			if (x2<0) {
753				x2 = 0;
754			}
755			if (y2<0) {
756				y2 = 0;
757			}
758			x2 += GEM_desk_x;
759			y2 += GEM_desk_y;
760
761			/* Destroy existing window */
762			if (GEM_handle >= 0) {
763				wind_close(GEM_handle);
764				wind_delete(GEM_handle);
765			}
766
767			/* Create window */
768			GEM_handle=wind_create(GEM_win_type, x2,y2,w2,h2);
769			if (GEM_handle<0) {
770				GEM_FreeBuffers(this);
771				SDL_SetError("Can not create window");
772				return NULL;
773			}
774
775#ifdef DEBUG_VIDEO_GEM
776			printf("sdl:video:gem: handle=%d\n", GEM_handle);
777#endif
778
779			/* Setup window name */
780			wind_set(GEM_handle,WF_NAME,(short)(((unsigned long)GEM_title_name)>>16),(short)(((unsigned long)GEM_title_name) & 0xffff),0,0);
781			GEM_refresh_name = SDL_FALSE;
782
783			/* Open the window */
784			wind_open(GEM_handle,x2,y2,w2,h2);
785		} else {
786			/* Resize window to fit asked video mode */
787			wind_get (GEM_handle, WF_WORKXYWH, &x2,&y2,&w2,&h2);
788			if (wind_calc(WC_BORDER, GEM_win_type, x2,y2,width,height, &x2,&y2,&w2,&h2)) {
789				wind_set (GEM_handle, WF_CURRXYWH, x2,y2,w2,h2);
790			}
791		}
792
793		GEM_fullscreen = SDL_FALSE;
794	}
795
796	/* Set up the new mode framebuffer */
797	current->w = width;
798	current->h = height;
799	if (use_shadow1) {
800		current->pixels = GEM_buffer1;
801		current->pitch = width * VDI_pixelsize;
802	} else {
803		current->pixels = VDI_screen;
804		current->pitch = VDI_pitch;
805	}
806
807#if SDL_VIDEO_OPENGL
808	if (flags & SDL_OPENGL) {
809		if (!SDL_AtariGL_Init(this, current)) {
810			GEM_FreeBuffers(this);
811			SDL_SetError("Can not create OpenGL context");
812			return NULL;
813		}
814
815		modeflags |= SDL_OPENGL;
816	}
817#endif
818
819	current->flags = modeflags;
820
821#ifdef DEBUG_VIDEO_GEM
822	printf("sdl:video:gem: surface: %dx%d\n", current->w, current->h);
823#endif
824
825	this->UpdateRects = GEM_UpdateRects;
826	GEM_lock_redraw = SDL_FALSE;	/* Enable redraw */
827
828	/* We're done */
829	return(current);
830}
831
832static int GEM_AllocHWSurface(_THIS, SDL_Surface *surface)
833{
834	return -1;
835}
836
837static void GEM_FreeHWSurface(_THIS, SDL_Surface *surface)
838{
839	return;
840}
841
842static int GEM_LockHWSurface(_THIS, SDL_Surface *surface)
843{
844	return(0);
845}
846
847static void GEM_UnlockHWSurface(_THIS, SDL_Surface *surface)
848{
849	return;
850}
851
852static void GEM_UpdateRectsFullscreen(_THIS, int numrects, SDL_Rect *rects)
853{
854	SDL_Surface *surface;
855	int i, surf_width;
856
857	surface = this->screen;
858	/* Need to be a multiple of 16 pixels */
859	surf_width=surface->w;
860	if ((surf_width & 15) != 0) {
861		surf_width = (surf_width | 15) + 1;
862	}
863
864	if (GEM_bufops & (B2S_C2P_1TO2|B2S_C2P_1TOS)) {
865		void *destscr;
866		int destpitch;
867
868		if (GEM_bufops & B2S_C2P_1TOS) {
869			destscr = VDI_screen;
870			destpitch = VDI_pitch;
871		} else {
872			destscr = GEM_buffer2;
873			destpitch = surface->pitch;
874		}
875
876		for (i=0;i<numrects;i++) {
877			void *source,*destination;
878			int x1,x2;
879
880			x1 = rects[i].x & ~15;
881			x2 = rects[i].x+rects[i].w;
882			if (x2 & 15) {
883				x2 = (x2 | 15) +1;
884			}
885
886			source = surface->pixels;
887			source += surface->pitch * rects[i].y;
888			source += x1;
889
890			destination = destscr;
891			destination += destpitch * rects[i].y;
892			destination += x1;
893
894			SDL_Atari_C2pConvert(
895				source, destination,
896				x2-x1, rects[i].h,
897				SDL_FALSE,
898				surface->pitch, destpitch
899			);
900		}
901	}
902
903	if (GEM_bufops & (B2S_VROCPYFM_1TOS|B2S_VROCPYFM_2TOS)) {
904		MFDB mfdb_src;
905		short blitcoords[8];
906
907		mfdb_src.fd_addr=surface->pixels;
908		mfdb_src.fd_w=surf_width;
909		mfdb_src.fd_h=surface->h;
910		mfdb_src.fd_wdwidth= (surface->pitch/VDI_pixelsize) >> 4;
911		mfdb_src.fd_nplanes=surface->format->BitsPerPixel;
912		mfdb_src.fd_stand=
913			mfdb_src.fd_r1=
914			mfdb_src.fd_r2=
915			mfdb_src.fd_r3= 0;
916		if (GEM_bufops & B2S_VROCPYFM_2TOS) {
917			mfdb_src.fd_addr=GEM_buffer2;
918		}
919
920		for ( i=0; i<numrects; ++i ) {
921			blitcoords[0] = blitcoords[4] = rects[i].x;
922			blitcoords[1] = blitcoords[5] = rects[i].y;
923			blitcoords[2] = blitcoords[6] = rects[i].x + rects[i].w - 1;
924			blitcoords[3] = blitcoords[7] = rects[i].y + rects[i].h - 1;
925
926			vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src, &VDI_dst_mfdb);
927		}
928	}
929}
930
931static void GEM_UpdateRectsWindowed(_THIS, int numrects, SDL_Rect *rects)
932{
933	short pxy[4], wind_pxy[4];
934	int i;
935
936	if (wind_get(GEM_handle, WF_WORKXYWH, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2], &wind_pxy[3])==0) {
937		return;
938	}
939
940	for ( i=0; i<numrects; ++i ) {
941		pxy[0] = wind_pxy[0] + rects[i].x;
942		pxy[1] = wind_pxy[1] + rects[i].y;
943		pxy[2] = rects[i].w;
944		pxy[3] = rects[i].h;
945
946		GEM_wind_redraw(this, GEM_handle, pxy);
947	}
948}
949
950static void GEM_UpdateRects(_THIS, int numrects, SDL_Rect *rects)
951{
952	SDL_Surface *surface;
953
954	if (GEM_lock_redraw) {
955		return;
956	}
957
958	surface = this->screen;
959
960	if (surface->flags & SDL_FULLSCREEN) {
961		GEM_UpdateRectsFullscreen(this, numrects, rects);
962	} else {
963		GEM_UpdateRectsWindowed(this, numrects, rects);
964	}
965}
966
967static int GEM_FlipHWSurfaceFullscreen(_THIS, SDL_Surface *surface)
968{
969	int surf_width;
970
971	/* Need to be a multiple of 16 pixels */
972	surf_width=surface->w;
973	if ((surf_width & 15) != 0) {
974		surf_width = (surf_width | 15) + 1;
975	}
976
977	if (GEM_bufops & (B2S_C2P_1TO2|B2S_C2P_1TOS)) {
978		void *destscr;
979		int destpitch;
980
981		if (GEM_bufops & B2S_C2P_1TOS) {
982			destscr = VDI_screen;
983			destpitch = VDI_pitch;
984		} else {
985			destscr = GEM_buffer2;
986			destpitch = surface->pitch;
987		}
988
989		SDL_Atari_C2pConvert(
990			surface->pixels, destscr,
991			surf_width, surface->h,
992			SDL_FALSE,
993			surface->pitch, destpitch
994		);
995	}
996
997	if (GEM_bufops & (B2S_VROCPYFM_1TOS|B2S_VROCPYFM_2TOS)) {
998		MFDB mfdb_src;
999		short blitcoords[8];
1000
1001		mfdb_src.fd_w=surf_width;
1002		mfdb_src.fd_h=surface->h;
1003		mfdb_src.fd_wdwidth=mfdb_src.fd_w >> 4;
1004		mfdb_src.fd_nplanes=surface->format->BitsPerPixel;
1005		mfdb_src.fd_stand=
1006			mfdb_src.fd_r1=
1007			mfdb_src.fd_r2=
1008			mfdb_src.fd_r3= 0;
1009		if (GEM_bufops & B2S_VROCPYFM_1TOS) {
1010			mfdb_src.fd_addr=surface->pixels;
1011		} else {
1012			mfdb_src.fd_addr=GEM_buffer2;
1013		}
1014
1015		blitcoords[0] = blitcoords[4] = 0;
1016		blitcoords[1] = blitcoords[5] = 0;
1017		blitcoords[2] = blitcoords[6] = surface->w - 1;
1018		blitcoords[3] = blitcoords[7] = surface->h - 1;
1019
1020		vro_cpyfm(VDI_handle, S_ONLY, blitcoords, &mfdb_src, &VDI_dst_mfdb);
1021	}
1022
1023	return(0);
1024}
1025
1026static int GEM_FlipHWSurfaceWindowed(_THIS, SDL_Surface *surface)
1027{
1028	short	pxy[8];
1029
1030	/* Update the whole window */
1031	wind_get(GEM_handle, WF_WORKXYWH, &pxy[0], &pxy[1], &pxy[2], &pxy[3]);
1032
1033	GEM_wind_redraw(this, GEM_handle, pxy);
1034
1035	return(0);
1036}
1037
1038static int GEM_FlipHWSurface(_THIS, SDL_Surface *surface)
1039{
1040	if (GEM_lock_redraw) {
1041		return(0);
1042	}
1043
1044	if (surface->flags & SDL_FULLSCREEN) {
1045		return GEM_FlipHWSurfaceFullscreen(this, surface);
1046	} else {
1047		return GEM_FlipHWSurfaceWindowed(this, surface);
1048	}
1049}
1050
1051static int GEM_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1052{
1053	int i;
1054	SDL_Surface *surface;
1055
1056#ifdef DEBUG_VIDEO_GEM
1057	printf("sdl:video:gem: setcolors()\n");
1058#endif
1059
1060	/* Do not change palette in True Colour */
1061	surface = this->screen;
1062	if (surface->format->BitsPerPixel > 8) {
1063		return 1;
1064	}
1065
1066	for(i = 0; i < ncolors; i++)
1067	{
1068		int		r, g, b;
1069		short	rgb[3];
1070
1071		r = colors[i].r;
1072		g = colors[i].g;
1073		b = colors[i].b;
1074
1075		rgb[0] = VDI_curpalette[i][0] = (1000 * r) / 255;
1076		rgb[1] = VDI_curpalette[i][1] =(1000 * g) / 255;
1077		rgb[2] = VDI_curpalette[i][2] =(1000 * b) / 255;
1078
1079		vs_color(VDI_handle, vdi_index[firstcolor+i], rgb);
1080	}
1081
1082	return(1);
1083}
1084
1085#if 0
1086static int GEM_ToggleFullScreen(_THIS, int on)
1087{
1088	if (on) {
1089		GEM_LockScreen(this);
1090	} else {
1091		GEM_UnlockScreen(this);
1092	}
1093
1094	return(1);
1095}
1096#endif
1097
1098/* Note:  If we are terminated, this could be called in the middle of
1099   another SDL video routine -- notably UpdateRects.
1100*/
1101void GEM_VideoQuit(_THIS)
1102{
1103	SDL_AtariXbios_RestoreVectors();
1104	if (GEM_usedevmouse) {
1105		SDL_AtariDevMouse_Close();
1106	}
1107
1108	GEM_FreeBuffers(this);
1109
1110#if SDL_VIDEO_OPENGL
1111	if (gl_active) {
1112		SDL_AtariGL_Quit(this, SDL_TRUE);
1113	}
1114#endif
1115
1116	/* Destroy window */
1117	if (GEM_handle>=0) {
1118		wind_close(GEM_handle);
1119		wind_delete(GEM_handle);
1120		GEM_handle=-1;
1121	}
1122
1123	GEM_UnlockScreen(this);
1124	if (GEM_menubar) {
1125		Mfree(GEM_menubar);
1126		GEM_menubar=NULL;
1127	}
1128
1129	appl_exit();
1130
1131	GEM_SetNewPalette(this, VDI_oldpalette);
1132
1133	/* Close VDI workstation */
1134	if (VDI_handle) {
1135		v_clsvwk(VDI_handle);
1136	}
1137
1138	/* Free mode list */
1139	if (SDL_modelist[0]) {
1140		SDL_free(SDL_modelist[0]);
1141		SDL_modelist[0]=NULL;
1142	}
1143
1144	this->screen->pixels = NULL;
1145}
1146
1147void GEM_wind_redraw(_THIS, int winhandle, short *inside)
1148{
1149	short todo[4];
1150
1151	/* Tell AES we are going to update */
1152	wind_update(BEG_UPDATE);
1153
1154	v_hide_c(VDI_handle);
1155
1156	/* Browse the rectangle list to redraw */
1157	if (wind_get(winhandle, WF_FIRSTXYWH, &todo[0], &todo[1], &todo[2], &todo[3])!=0) {
1158
1159		while (todo[2] && todo[3]) {
1160
1161			if (rc_intersect((GRECT *)inside,(GRECT *)todo)) {
1162				todo[2] += todo[0]-1;
1163				todo[3] += todo[1]-1;
1164				refresh_window(this, winhandle, todo);
1165			}
1166
1167			if (wind_get(winhandle, WF_NEXTXYWH, &todo[0], &todo[1], &todo[2], &todo[3])==0) {
1168				break;
1169			}
1170		}
1171
1172	}
1173
1174	/* Update finished */
1175	wind_update(END_UPDATE);
1176
1177	v_show_c(VDI_handle,1);
1178}
1179
1180static void refresh_window(_THIS, int winhandle, short *rect)
1181{
1182	MFDB mfdb_src;
1183	short pxy[8],wind_pxy[8];
1184	SDL_Surface *surface;
1185	int iconified;
1186
1187	/* Is window iconified ? */
1188	iconified = 0;
1189/*	if (GEM_wfeatures & (1<<WF_ICONIFY))*/ {
1190		if (wind_get(winhandle, WF_ICONIFY, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2], &wind_pxy[3])!=0) {
1191			iconified = wind_pxy[0];
1192		}
1193	}
1194
1195	if (wind_get(winhandle, WF_WORKXYWH, &wind_pxy[0], &wind_pxy[1], &wind_pxy[2], &wind_pxy[3])==0) {
1196		return;
1197	}
1198
1199	if (iconified && GEM_icon) {
1200		short icon_rect[4], dst_rect[4];
1201		short iconx,icony;
1202
1203		surface = GEM_icon;
1204
1205		GEM_ClearRect(this, rect);
1206
1207		/* Calculate centered icon(x,y,w,h) relative to window */
1208		iconx = (wind_pxy[2]-surface->w)>>1;
1209		icony = (wind_pxy[3]-surface->h)>>1;
1210
1211		icon_rect[0] = iconx;
1212		icon_rect[1] = icony;
1213		icon_rect[2] = surface->w;
1214		icon_rect[3] = surface->h;
1215
1216		/* Calculate redraw rectangle(x,y,w,h) relative to window */
1217		dst_rect[0] = rect[0]-wind_pxy[0];
1218		dst_rect[1] = rect[1]-wind_pxy[1];
1219		dst_rect[2] = rect[2]-rect[0]+1;
1220		dst_rect[3] = rect[3]-rect[1]+1;
1221
1222		/* Does the icon rectangle must be redrawn ? */
1223		if (!rc_intersect((GRECT *)icon_rect, (GRECT *)dst_rect)) {
1224			return;
1225		}
1226
1227#if DEBUG_VIDEO_GEM
1228		printf("sdl:video:gem:  clip(0,0,%d,%d) to (%d,%d,%d,%d)\n",
1229			surface->w-1,surface->h-1, dst_rect[0],dst_rect[1],dst_rect[2],dst_rect[3]);
1230		printf("sdl:video:gem:  icon(%d,%d,%d,%d)\n",
1231			icon_rect[0], icon_rect[1], icon_rect[2], icon_rect[3]);
1232		printf("sdl:video:gem: refresh_window(): draw icon\n");
1233#endif
1234
1235		/* Calculate icon(x1,y1,x2,y2) relative to screen */
1236		icon_rect[0] += wind_pxy[0];
1237		icon_rect[1] += wind_pxy[1];
1238		icon_rect[2] += icon_rect[0]-1;
1239		icon_rect[3] += icon_rect[1]-1;
1240
1241		/* Calculate intersection rectangle to redraw */
1242		pxy[4]=pxy[0]=MAX(icon_rect[0],rect[0]);
1243		pxy[5]=pxy[1]=MAX(icon_rect[1],rect[1]);
1244 		pxy[6]=pxy[2]=MIN(icon_rect[2],rect[2]);
1245	 	pxy[7]=pxy[3]=MIN(icon_rect[3],rect[3]);
1246
1247		/* Calculate icon source image pos relative to window */
1248		pxy[0] -= wind_pxy[0]+iconx;
1249		pxy[1] -= wind_pxy[1]+icony;
1250		pxy[2] -= wind_pxy[0]+iconx;
1251		pxy[3] -= wind_pxy[1]+icony;
1252
1253	} else {
1254		surface = this->screen;
1255
1256#if DEBUG_VIDEO_GEM
1257		printf("sdl:video:gem: refresh_window(): draw frame buffer\n");
1258#endif
1259
1260		/* Redraw all window content */
1261		pxy[0] = rect[0]-wind_pxy[0];
1262		pxy[1] = rect[1]-wind_pxy[1];
1263	 	pxy[2] = rect[2]-wind_pxy[0];
1264	 	pxy[3] = rect[3]-wind_pxy[1];
1265
1266		pxy[4] = rect[0];
1267		pxy[5] = rect[1];
1268		pxy[6] = rect[2];
1269		pxy[7] = rect[3];
1270	}
1271
1272	if (GEM_bufops & B2S_C2P_1TO2) {
1273		void *src, *dest;
1274		int x1,x2;
1275
1276		x1 = (rect[0]-wind_pxy[0]) & ~15;
1277		x2 = rect[2]-wind_pxy[0];
1278		if (x2 & 15) {
1279			x2 = (x2 | 15) +1;
1280		}
1281
1282		src = surface->pixels;
1283		src += surface->pitch * (rect[1]-wind_pxy[1]);
1284		src += x1;
1285
1286		dest = GEM_buffer2;
1287		dest += surface->pitch * (rect[1]-wind_pxy[1]);
1288		dest += x1;
1289
1290		SDL_Atari_C2pConvert(
1291			src, dest,
1292			x2-x1, rect[3]-rect[1]+1,
1293			SDL_FALSE,
1294			surface->pitch, surface->pitch
1295		);
1296	}
1297
1298	mfdb_src.fd_addr=surface->pixels;
1299	{
1300		int width;
1301
1302		/* Need to be a multiple of 16 pixels */
1303		width=surface->w;
1304		if ((width & 15) != 0) {
1305			width = (width | 15) + 1;
1306		}
1307		mfdb_src.fd_w=width;
1308	}
1309	mfdb_src.fd_h=surface->h;
1310  	mfdb_src.fd_nplanes=surface->format->BitsPerPixel;
1311	mfdb_src.fd_wdwidth=mfdb_src.fd_w>>4;
1312	mfdb_src.fd_stand=
1313		mfdb_src.fd_r1=
1314  		mfdb_src.fd_r2=
1315	  	mfdb_src.fd_r3= 0;
1316
1317	if (GEM_bufops & B2S_VROCPYFM_2TOS) {
1318		mfdb_src.fd_addr=GEM_buffer2;
1319	}
1320
1321#if DEBUG_VIDEO_GEM
1322	printf("sdl:video:gem: redraw %dx%d: (%d,%d,%d,%d) to (%d,%d,%d,%d)\n",
1323		surface->w, surface->h,
1324		pxy[0],pxy[1],pxy[2],pxy[3],
1325		pxy[4],pxy[5],pxy[6],pxy[7]
1326	);
1327#endif
1328
1329	vro_cpyfm( VDI_handle, S_ONLY, pxy, &mfdb_src, &VDI_dst_mfdb);
1330}
1331
1332#if SDL_VIDEO_OPENGL
1333
1334static void GEM_GL_SwapBuffers(_THIS)
1335{
1336	SDL_AtariGL_SwapBuffers(this);
1337	GEM_FlipHWSurface(this, this->screen);
1338}
1339
1340#endif
1341