1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4    Copyright (C) 2001  Hsieh-Fu Tsai
5    Copyright (C) 2002  Greg Haerr <greg@censoft.com>
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with this library; if not, write to the Free
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21    Sam Lantinga
22    slouken@libsdl.org
23
24    Hsieh-Fu Tsai
25    clare@setabox.com
26*/
27#include "SDL_config.h"
28
29#include "SDL_thread.h"
30#include "SDL_video.h"
31#include "../SDL_pixels_c.h"
32#include "../../events/SDL_events_c.h"
33
34#define MWINCLUDECOLORS
35#include "SDL_nxvideo.h"
36#include "SDL_nxmodes_c.h"
37#include "SDL_nxwm_c.h"
38#include "SDL_nxmouse_c.h"
39#include "SDL_nximage_c.h"
40#include "SDL_nxevents_c.h"
41
42// Initialization/Query functions
43static int NX_VideoInit (_THIS, SDL_PixelFormat * vformat) ;
44static SDL_Surface * NX_SetVideoMode (_THIS, SDL_Surface * current, int width, int height, int bpp, Uint32 flags) ;
45static int NX_SetColors (_THIS, int firstcolor, int ncolors, SDL_Color * colors) ;
46static void NX_VideoQuit (_THIS) ;
47static void NX_DestroyWindow (_THIS, SDL_Surface * screen) ;
48static int NX_ToggleFullScreen (_THIS, int on) ;
49static void NX_UpdateMouse (_THIS) ;
50static int NX_SetGammaRamp (_THIS, Uint16 * ramp) ;
51static int NX_GetGammaRamp (_THIS, Uint16 * ramp) ;
52
53// Microwin driver bootstrap functions
54static int NX_Available ()
55{
56    Dprintf ("enter NX_Available\n") ;
57
58    if (GrOpen () < 0) return 0 ;
59        GrClose () ;
60
61    Dprintf ("leave NX_Available\n") ;
62    return 1 ;
63}
64
65static void NX_DeleteDevice (SDL_VideoDevice * device)
66{
67    Dprintf ("enter NX_DeleteDevice\n") ;
68
69    if (device) {
70        if (device -> hidden) SDL_free (device -> hidden) ;
71        if (device -> gl_data) SDL_free (device -> gl_data) ;
72            SDL_free (device) ;
73    }
74
75    Dprintf ("leave NX_DeleteDevice\n") ;
76}
77
78static SDL_VideoDevice * NX_CreateDevice (int devindex)
79{
80    SDL_VideoDevice * device ;
81
82    Dprintf ("enter NX_CreateDevice\n") ;
83
84    // Initialize all variables that we clean on shutdown
85    device = (SDL_VideoDevice *) SDL_malloc (sizeof (SDL_VideoDevice)) ;
86    if (device) {
87        SDL_memset (device, 0, (sizeof * device)) ;
88        device -> hidden = (struct SDL_PrivateVideoData *)
89                SDL_malloc ((sizeof * device -> hidden)) ;
90        device -> gl_data = NULL ;
91    }
92    if ((device == NULL) || (device -> hidden == NULL)) {
93        SDL_OutOfMemory () ;
94        NX_DeleteDevice (device) ;
95        return 0 ;
96    }
97    SDL_memset (device -> hidden, 0, (sizeof * device -> hidden)) ;
98
99    // Set the function pointers
100    device -> VideoInit = NX_VideoInit ;
101    device -> ListModes = NX_ListModes ;
102    device -> SetVideoMode = NX_SetVideoMode ;
103    device -> ToggleFullScreen = NX_ToggleFullScreen ;
104    device -> UpdateMouse = NX_UpdateMouse ;
105    device -> CreateYUVOverlay = NULL ;
106    device -> SetColors = NX_SetColors ;
107    device -> UpdateRects = NULL ;
108    device -> VideoQuit = NX_VideoQuit;
109    device -> AllocHWSurface = NULL ;
110    device -> CheckHWBlit = NULL ;
111    device -> FillHWRect = NULL ;
112    device -> SetHWColorKey = NULL ;
113    device -> SetHWAlpha = NULL ;
114    device -> LockHWSurface = NULL ;
115    device -> UnlockHWSurface = NULL ;
116    device -> FlipHWSurface = NULL ;
117    device -> FreeHWSurface = NULL ;
118    device -> SetGamma = NULL ;
119    device -> GetGamma = NULL ;
120    device -> SetGammaRamp = NX_SetGammaRamp ;
121    device -> GetGammaRamp = NX_GetGammaRamp ;
122
123#if SDL_VIDEO_OPENGL
124    device -> GL_LoadLibrary = NULL ;
125    device -> GL_GetProcAddress = NULL ;
126    device -> GL_GetAttribute = NULL ;
127    device -> GL_MakeCurrent = NULL ;
128    device -> GL_SwapBuffers = NULL ;
129#endif
130
131    device -> SetIcon = NULL ;
132    device -> SetCaption = NX_SetCaption;
133    device -> IconifyWindow = NULL ;
134    device -> GrabInput = NULL ;
135    device -> GetWMInfo = NX_GetWMInfo ;
136    device -> FreeWMCursor =  NX_FreeWMCursor ;
137    device -> CreateWMCursor = NX_CreateWMCursor ;
138    device -> ShowWMCursor = NX_ShowWMCursor ;
139    device -> WarpWMCursor = NX_WarpWMCursor ;
140    device -> CheckMouseMode = NULL ;
141    device -> InitOSKeymap = NX_InitOSKeymap ;
142    device -> PumpEvents = NX_PumpEvents ;
143
144    device -> free = NX_DeleteDevice ;
145
146    Dprintf ("leave NX_CreateDevice\n") ;
147    return device ;
148}
149
150VideoBootStrap NX_bootstrap = {
151    "nanox", "nanox", NX_Available, NX_CreateDevice
152} ;
153
154static void create_aux_windows (_THIS)
155{
156    GR_WM_PROPERTIES props ;
157
158    Dprintf ("enter create_aux_windows\n") ;
159
160    // Don't create any extra windows if we are being managed
161    if (SDL_windowid) {
162        FSwindow = 0 ;
163        return ;
164    }
165
166    if (FSwindow && FSwindow != GR_ROOT_WINDOW_ID) {
167        GrDestroyWindow (FSwindow) ;
168    }
169
170    FSwindow = GrNewWindow (GR_ROOT_WINDOW_ID, 0, 0, 1, 1, 0, BLACK, BLACK) ;
171    props.flags = GR_WM_FLAGS_PROPS ;
172    props.props = GR_WM_PROPS_NODECORATE ;
173    GrSetWMProperties (FSwindow, & props) ;
174
175    GrSelectEvents (FSwindow, (GR_EVENT_MASK_EXPOSURE         |
176        GR_EVENT_MASK_BUTTON_DOWN  | GR_EVENT_MASK_BUTTON_UP  |
177        GR_EVENT_MASK_FOCUS_IN     | GR_EVENT_MASK_FOCUS_OUT  |
178        GR_EVENT_MASK_KEY_DOWN     | GR_EVENT_MASK_KEY_UP     |
179        GR_EVENT_MASK_MOUSE_ENTER  | GR_EVENT_MASK_MOUSE_EXIT |
180        GR_EVENT_MASK_MOUSE_MOTION | GR_EVENT_MASK_UPDATE     |
181        GR_EVENT_MASK_CLOSE_REQ)) ;
182
183    Dprintf ("leave create_aux_windows\n") ;
184}
185
186int NX_VideoInit (_THIS, SDL_PixelFormat * vformat)
187{
188    GR_SCREEN_INFO si ;
189
190    Dprintf ("enter NX_VideoInit\n") ;
191
192    if (GrOpen () < 0) {
193        SDL_SetError ("GrOpen() fail") ;
194        return -1 ;
195    }
196
197    // use share memory to speed up
198#ifdef NANOX_SHARE_MEMORY
199    GrReqShmCmds (0xFFFF);
200#endif
201
202    SDL_Window = 0 ;
203    FSwindow = 0 ;
204
205    GammaRamp_R = NULL ;
206    GammaRamp_G = NULL ;
207    GammaRamp_B = NULL ;
208
209    GrGetScreenInfo (& si) ;
210    SDL_Visual.bpp = si.bpp ;
211
212    /* Determine the current screen size */
213    this->info.current_w = si.cols ;
214    this->info.current_h = si.rows ;
215
216    // GetVideoMode
217    SDL_modelist = (SDL_Rect **) SDL_malloc (sizeof (SDL_Rect *) * 2) ;
218    if (SDL_modelist) {
219        SDL_modelist [0] = (SDL_Rect *) SDL_malloc (sizeof(SDL_Rect)) ;
220        if (SDL_modelist [0]) {
221            SDL_modelist [0] -> x = 0 ;
222            SDL_modelist [0] -> y = 0 ;
223            SDL_modelist [0] -> w = si.cols ;
224            SDL_modelist [0] -> h = si.rows ;
225        }
226        SDL_modelist [1] = NULL ;
227    }
228
229    pixel_type = si.pixtype;
230    SDL_Visual.red_mask = si.rmask;
231    SDL_Visual.green_mask = si.gmask;
232    SDL_Visual.blue_mask = si.bmask;
233
234    vformat -> BitsPerPixel = SDL_Visual.bpp ;
235    if (vformat -> BitsPerPixel > 8) {
236        vformat -> Rmask = SDL_Visual.red_mask ;
237        vformat -> Gmask = SDL_Visual.green_mask ;
238        vformat -> Bmask = SDL_Visual.blue_mask ;
239    }
240
241    // See if we have been passed a window to use
242    SDL_windowid = getenv ("SDL_WINDOWID") ;
243
244    // Create the fullscreen (and managed windows : no implement)
245    create_aux_windows (this) ;
246
247    Dprintf ("leave NX_VideoInit\n") ;
248    return 0 ;
249}
250
251void NX_VideoQuit (_THIS)
252{
253    Dprintf ("enter NX_VideoQuit\n") ;
254
255    // Start shutting down the windows
256    NX_DestroyImage (this, this -> screen) ;
257    NX_DestroyWindow (this, this -> screen) ;
258    if (FSwindow && FSwindow != GR_ROOT_WINDOW_ID) {
259        GrDestroyWindow (FSwindow) ;
260    }
261    NX_FreeVideoModes (this) ;
262    SDL_free (GammaRamp_R) ;
263    SDL_free (GammaRamp_G) ;
264    SDL_free (GammaRamp_B) ;
265
266#ifdef ENABLE_NANOX_DIRECT_FB
267    if (Clientfb)
268        GrCloseClientFramebuffer();
269#endif
270    GrClose () ;
271
272    Dprintf ("leave NX_VideoQuit\n") ;
273}
274
275static void NX_DestroyWindow (_THIS, SDL_Surface * screen)
276{
277    Dprintf ("enter NX_DestroyWindow\n") ;
278
279    if (! SDL_windowid) {
280        if (screen && (screen -> flags & SDL_FULLSCREEN)) {
281            screen -> flags &= ~ SDL_FULLSCREEN ;
282            NX_LeaveFullScreen (this) ;
283        }
284
285        // Destroy the output window
286        if (SDL_Window && SDL_Window != GR_ROOT_WINDOW_ID) {
287            GrDestroyWindow (SDL_Window) ;
288        }
289    }
290
291    // Free the graphics context
292    if (! SDL_GC) {
293        GrDestroyGC (SDL_GC) ;
294        SDL_GC = 0;
295    }
296
297    Dprintf ("leave NX_DestroyWindow\n") ;
298}
299
300static int NX_CreateWindow (_THIS, SDL_Surface * screen,
301                int w, int h, int bpp, Uint32 flags)
302{
303    Dprintf ("enter NX_CreateWindow\n") ;
304
305    // If a window is already present, destroy it and start fresh
306    if (SDL_Window && SDL_Window != GR_ROOT_WINDOW_ID) {
307        NX_DestroyWindow (this, screen) ;
308    }
309
310    // See if we have been given a window id
311    if (SDL_windowid) {
312        SDL_Window = SDL_strtol (SDL_windowid, NULL, 0) ;
313    } else {
314        SDL_Window = 0 ;
315    }
316
317    if ( ! SDL_ReallocFormat (screen, bpp, SDL_Visual.red_mask,
318        SDL_Visual.green_mask, SDL_Visual.blue_mask, 0))
319        return -1;
320
321    // Create (or use) the nanox display window
322    if (! SDL_windowid) {
323
324        SDL_Window = GrNewWindow (GR_ROOT_WINDOW_ID, 0, 0, w, h, 0, BLACK, WHITE) ;
325
326        GrSelectEvents (SDL_Window, (GR_EVENT_MASK_EXPOSURE       |
327            GR_EVENT_MASK_BUTTON_DOWN  | GR_EVENT_MASK_BUTTON_UP  |
328            GR_EVENT_MASK_FOCUS_IN     | GR_EVENT_MASK_FOCUS_OUT  |
329            GR_EVENT_MASK_KEY_DOWN     | GR_EVENT_MASK_KEY_UP     |
330            GR_EVENT_MASK_MOUSE_ENTER  | GR_EVENT_MASK_MOUSE_EXIT |
331            GR_EVENT_MASK_MOUSE_MOTION | GR_EVENT_MASK_UPDATE     |
332            GR_EVENT_MASK_CLOSE_REQ)) ;
333    }
334
335    /* Create the graphics context here, once we have a window */
336    SDL_GC = GrNewGC () ;
337    if (SDL_GC == 0) {
338        SDL_SetError("Couldn't create graphics context");
339        return(-1);
340    }
341
342    // Map them both and go fullscreen, if requested
343    if (! SDL_windowid) {
344        GrMapWindow (SDL_Window) ;
345        if (flags & SDL_FULLSCREEN) {
346            screen -> flags |= SDL_FULLSCREEN ;
347            NX_EnterFullScreen (this) ;
348        } else {
349            screen -> flags &= ~ SDL_FULLSCREEN ;
350        }
351    }
352
353#ifdef ENABLE_NANOX_DIRECT_FB
354    /* attempt allocating the client side framebuffer */
355    Clientfb = GrOpenClientFramebuffer();
356    /* NULL return will default to using GrArea()*/
357#endif
358
359    Dprintf ("leave NX_CreateWindow\n") ;
360    return 0 ;
361}
362
363SDL_Surface * NX_SetVideoMode (_THIS, SDL_Surface * current,
364                int width, int height, int bpp, Uint32 flags)
365{
366    Dprintf ("enter NX_SetVideoMode\n") ;
367
368    // Lock the event thread, in multi-threading environments
369    SDL_Lock_EventThread () ;
370
371    bpp = SDL_Visual.bpp ;
372    if (NX_CreateWindow (this, current, width, height, bpp, flags) < 0) {
373        current = NULL;
374        goto done;
375    }
376
377    if (current -> w != width || current -> h != height) {
378        current -> w = width ;
379        current -> h = height ;
380        current -> pitch = SDL_CalculatePitch (current) ;
381        NX_ResizeImage (this, current, flags) ;
382    }
383
384    /* Clear these flags and set them only if they are in the new set. */
385    current -> flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
386    current -> flags |= (flags & (SDL_RESIZABLE | SDL_NOFRAME)) ;
387
388  done:
389    SDL_Unlock_EventThread () ;
390
391    Dprintf ("leave NX_SetVideoMode\n") ;
392
393    // We're done!
394    return current ;
395}
396
397// ncolors <= 256
398int NX_SetColors (_THIS, int firstcolor, int ncolors, SDL_Color * colors)
399{
400    int        i ;
401    GR_PALETTE pal ;
402
403    Dprintf ("enter NX_SetColors\n") ;
404
405    if (ncolors > 256) return 0 ;
406
407    pal.count = ncolors ;
408    for (i = 0; i < ncolors; ++ i) {
409        pal.palette [i].r = colors [i].r ;
410        pal.palette [i].g = colors [i].g ;
411        pal.palette [i].b = colors [i].b ;
412    }
413    GrSetSystemPalette (firstcolor, & pal) ;
414
415    Dprintf ("leave NX_SetColors\n") ;
416    return 1 ;
417}
418
419static int NX_ToggleFullScreen (_THIS, int on)
420{
421    SDL_Rect rect ;
422    Uint32   event_thread ;
423
424    Dprintf ("enter NX_ToggleFullScreen\n") ;
425
426    // Don't switch if we don't own the window
427    if (SDL_windowid) return 0 ;
428
429    // Don't lock if we are the event thread
430    event_thread = SDL_EventThreadID () ;
431    if (event_thread && (SDL_ThreadID () == event_thread)) {
432        event_thread = 0 ;
433    }
434    if (event_thread) {
435        SDL_Lock_EventThread() ;
436    }
437
438    if (on) {
439        NX_EnterFullScreen (this) ;
440    } else {
441        this -> screen -> flags &= ~ SDL_FULLSCREEN ;
442        NX_LeaveFullScreen (this) ;
443    }
444
445    rect.x = rect.y = 0 ;
446    rect.w = this -> screen -> w, rect.h = this -> screen -> h ;
447    NX_NormalUpdate (this, 1, & rect) ;
448
449    if (event_thread) {
450        SDL_Unlock_EventThread () ;
451    }
452
453    Dprintf ("leave NX_ToggleFullScreen\n") ;
454    return 1 ;
455}
456
457// Update the current mouse state and position
458static void NX_UpdateMouse (_THIS)
459{
460    int            x, y ;
461    GR_WINDOW_INFO info ;
462    GR_SCREEN_INFO si ;
463
464
465    Dprintf ("enter NX_UpdateMouse\n") ;
466
467    // Lock the event thread, in multi-threading environments
468    SDL_Lock_EventThread () ;
469
470    GrGetScreenInfo (& si) ;
471    GrGetWindowInfo (SDL_Window, & info) ;
472    x = si.xpos - info.x ;
473    y = si.ypos - info.y ;
474    if (x >= 0 && x <= info.width && y >= 0 && y <= info.height) {
475        SDL_PrivateAppActive (1, SDL_APPMOUSEFOCUS) ;
476        SDL_PrivateMouseMotion (0, 0, x, y);
477    } else {
478        SDL_PrivateAppActive (0, SDL_APPMOUSEFOCUS) ;
479    }
480
481    SDL_Unlock_EventThread () ;
482    Dprintf ("leave NX_UpdateMouse\n") ;
483}
484
485static int NX_SetGammaRamp (_THIS, Uint16 * ramp)
486{
487    int i ;
488    Uint16 * red, * green, * blue ;
489
490    Dprintf ("enter NX_SetGammaRamp\n") ;
491
492    if (SDL_Visual.bpp != 32 && SDL_Visual.bpp != 24) return -1 ;
493
494    if (! GammaRamp_R) GammaRamp_R = (Uint16 *) SDL_malloc (sizeof (Uint16) * CI_SIZE) ;
495    if (! GammaRamp_G) GammaRamp_G = (Uint16 *) SDL_malloc (sizeof (Uint16) * CI_SIZE) ;
496    if (! GammaRamp_B) GammaRamp_B = (Uint16 *) SDL_malloc (sizeof (Uint16) * CI_SIZE) ;
497    if ((! GammaRamp_R) || (! GammaRamp_G) || (! GammaRamp_B)) {
498        SDL_OutOfMemory () ;
499        return -1 ;
500    }
501
502    for (i = 0; i < CI_SIZE; ++ i)
503        GammaRamp_R [i] = GammaRamp_G [i] = GammaRamp_B [i] = i ;
504
505    red   = ramp ;
506    green = ramp + CI_SIZE ;
507    blue  = green + CI_SIZE ;
508
509    for (i = 0; i < CI_SIZE; ++ i) {
510        GammaRamp_R [i] = red   [i] ;
511        GammaRamp_G [i] = green [i] ;
512        GammaRamp_B [i] = blue  [i] ;
513    }
514    SDL_UpdateRect(this->screen, 0, 0, 0, 0);
515
516    Dprintf ("leave NX_SetGammaRamp\n") ;
517    return 0 ;
518}
519
520static int NX_GetGammaRamp (_THIS, Uint16 * ramp)
521{
522    int i ;
523    Uint16 * red, * green, * blue ;
524
525    Dprintf ("enter NX_GetGammaRamp\n") ;
526
527    if (SDL_Visual.bpp != 32 && SDL_Visual.bpp != 24) return -1 ;
528    red   = ramp ;
529    green = ramp  + CI_SIZE ;
530    blue  = green + CI_SIZE ;
531    if (GammaRamp_R && GammaRamp_G && GammaRamp_B) {
532        for (i = 0; i < CI_SIZE; ++ i) {
533            red   [i] = GammaRamp_R [i] ;
534            green [i] = GammaRamp_G [i] ;
535            blue  [i] = GammaRamp_B [i] ;
536        }
537    } else {
538        for (i = 0; i < CI_SIZE; ++ i)
539            red [i] = green [i] = blue [i] = i ;
540    }
541
542    Dprintf ("leave NX_GetGammaRamp\n") ;
543    return 0 ;
544}
545