8 #include <time.h> /* for save_screenshot()'s calls */
10 #include "SDL_image.h"
16 #include "resource/resource.h"
18 static void load_icon(const char *file
);
19 static Uint32
get_flags(const struct smode_t
*smode
);
20 static SDL_Surface
*set_graphics_mode(const struct smode_t
*smode
);
21 static int try_resizing_screen(struct smode_t
*smode
);
22 static void call_blit_surface(SDL_Surface
*from
, SDL_Rect
*fromrect
,
23 SDL_Surface
*to
, SDL_Rect
*torect
);
24 static void fill_area_no_alpha(SDL_Surface
*screen
,
25 int x
, int y
, int w
, int h
, Uint8 r
, Uint8 g
, Uint8 b
);
26 static Uint32
get_pixel(SDL_Surface
*surface
, int x
, int y
);
28 void restrict_int(int *value
, int max
) {
29 if(*value
< 0) *value
= 0;
30 else if(*value
> max
) *value
= max
;
33 void restrict_pos(double *pos
) {
34 if(*pos
< 0.0) *pos
= 0.0;
35 else if(*pos
> 100.0) *pos
= 100.0;
38 void wrap_int(int *value
, int max
) {
39 if(*value
< 0) *value
= max
- (-*value
% max
);
43 void init_smode(struct smode_t
*smode
, struct resource_t
*settings
) {
45 smode
->width
= (int)lookup_resource_number(settings
, 640,
46 "xuni-resource", "screenmode", "width", 0);
47 smode
->height
= (int)lookup_resource_number(settings
, 480,
48 "xuni-resource", "screenmode", "height", 0);
49 smode
->depth
= (int)lookup_resource_number(settings
, 0,
50 "xuni-resource", "screenmode", "depth", 0);
51 smode
->fullscreen
= (int)lookup_resource_number(settings
, 0,
52 "xuni-resource", "screenmode", "fullscreen", 0);
56 smode
->restrictfocus
= (int)lookup_resource_number(settings
, 0,
57 "xuni-resource", "screenmode", "restrict", 0);
60 /*! Initializes the SDL and SDL_ttf. Also sets the screen mode, sets the
61 caption to "Loading", disables SDL UNICODE translation, sets the focus,
62 enables key repeating, loads the icon for the window, etc.
63 \param smode The screen mode structure to read the screen mode from.
64 \param icon The icon to use for the xuni application window.
66 void init_sdl_libraries(struct smode_t
*smode
, const char *icon
) {
67 if(SDL_Init(SDL_INIT_VIDEO
| SDL_INIT_TIMER
) < 0) {
68 log_message(ERROR_TYPE_FATAL
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
74 smode
->screen
= set_graphics_mode(smode
);
76 /* try default screen mode? */
77 log_message(ERROR_TYPE_FATAL
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
78 "Can't set graphics mode %ix%ix%i (%s)",
79 smode
->width
, smode
->height
, smode
->depth
,
80 smode
->fullscreen
? "fullscreen" : "windowed");
83 set_caption("Loading . . .");
85 smode
->restrictfocus
= set_focus(smode
->restrictfocus
);
87 focus_changed(smode
, SDL_APPACTIVE
);
92 /* !!! this doesn't have to be a fatal error */
93 log_message(ERROR_TYPE_FATAL
, 0, __FILE__
, __LINE__
,
94 "Can't init SDL_ttf: %s", TTF_GetError());
97 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY
,
98 SDL_DEFAULT_REPEAT_INTERVAL
);
101 /*! Tries to set the input grab mode ("focus") to \a mode, but only if the
102 current grab mode differs from the requested grab mode. If the modes are
103 the same, it does nothing. Regardless, it returns the new grab mode.
104 \param mode The mode to set the grab mode ("focus") to.
105 \return The new grab mode. This could differ from \a mode if the function
106 failed to set the grab mode.
108 SDL_GrabMode
set_focus(SDL_GrabMode mode
) {
109 if(SDL_WM_GrabInput(SDL_GRAB_QUERY
) != mode
) {
110 return SDL_WM_GrabInput(mode
);
116 /*! Determines whether the xuni windows has a specific type of focus or not.
117 \param focus The OR'd focus flags to check for, SDL_APP*.
118 \return True if the window has the focus specified in \a focus.
120 int focus_in(Uint8 focus
) {
121 return (SDL_GetAppState() & focus
) == focus
;
124 /*! Sets \a smode->focus, based on the flags in \a focus.
125 \param smode The structure which contains the \c focus member to set.
126 \param focus The focus which, if true, to consider the xuni window active.
127 \return Nonzero if the focus has changed.
129 int focus_changed(struct smode_t
*smode
, Uint8 focus
) {
130 /*printf("focus_changed(): mouse=%i input=%i active=%i\n",
131 SDL_GetAppState() & SDL_APPMOUSEFOCUS,
132 SDL_GetAppState() & SDL_APPINPUTFOCUS,
133 SDL_GetAppState() & SDL_APPACTIVE);*/
135 if(focus_in(focus
)) {
141 else if(smode
->focus
) {
149 /*! Uninitializes the SDL and SDL_ttf libraries; the other SDL libraries
150 require no such de-initialization.
152 void quit_sdl_libraries(void) {
157 void free_smode(struct smode_t
*smode
) {
161 /*! Frees the memory allocated for a theme_t structure.
162 \param theme The theme_t structure to free the contents of.
164 void free_theme(struct theme_t
*theme
) {
165 SDL_FreeCursor(theme
->cursors
.text
);
168 /*! Loads and sets an icon for the SDL window. If the icon could not be
169 opened, prints a warning instead.
171 \param file The file to use as an icon, in any format supported by
172 SDL_image. Note that Windows icons must be 32x32 to work correctly.
174 static void load_icon(const char *file
) {
179 icon
= IMG_Load(file
);
182 SDL_WM_SetIcon(icon
, NULL
);
183 SDL_FreeSurface(icon
);
186 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
187 "Can't open application icon \"%s\"", file
);
191 /*! Save screenshots to shot_<time>[_<number>].bmp, where number is the Nth
192 screenshot this second. A number is only appended if a screenshot has
193 already been taken this second.
195 time is of the format mm-dd-yy_at_hh-mm-ss; hours are in 24-hour time.
197 This function is not thread-safe, because it calls localtime().
199 \param screen The surface to save to the file (usually the main screen).
201 void save_screenshot(SDL_Surface
*screen
) {
202 char filename
[BUFSIZ
];
203 static time_t prevt
= (time_t)-1; /* error return value of mktime() */
204 static int count
= 0;
205 time_t t
= time(NULL
);
206 struct tm
*local
= localtime(&t
);
209 pos
= sprintf(filename
, "shot_%02i-%02i-%02i_at_%02i-%02i-%02i",
210 local
->tm_mday
, local
->tm_mon
+ 1, local
->tm_year
% 100,
211 local
->tm_hour
, local
->tm_min
, local
->tm_sec
);
214 /* +1 to start at _2 */
215 pos
+= sprintf(filename
+ pos
, "_%i", ++count
+ 1);
219 prevt
= t
; /* hopefully time_ts can be assigned */
221 strcpy(filename
+ pos
, ".bmp");
223 if(SDL_SaveBMP(screen
, filename
)) {
224 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
225 "Can't save screenshot \"%s\"", filename
);
228 log_message(ERROR_TYPE_LOG
, 0, __FILE__
, __LINE__
,
229 "Screenshot saved as \"%s\"", filename
);
231 /* perhaps compress the screenshot here? */
235 void paint_cursor(struct xuni_t
*xuni
, struct widget_t
*cursor
) {
238 if(!cursor
|| !focus_in(SDL_APPMOUSEFOCUS
)) return;
240 SDL_GetMouseState(&xp
, &yp
);
242 prepare_paint_image(xuni
, cursor
);
244 blit_surface(xuni
->smode
->screen
,
245 cursor
->p
.image
->image
,
246 xp
- cursor
->p
.image
->image
->w
/ 2,
247 yp
- cursor
->p
.image
->image
->h
/ 2);
250 void show_cursor(int enable
) {
251 int prev
= SDL_ShowCursor(enable
? SDL_ENABLE
: SDL_DISABLE
);
253 /* Handles a bug in the X11 video driver: when the cursor is disabled,
254 moved, and then re-enabled, it is painted in the original position,
255 but otherwise the SDL thinks it is in the new position. Calling
256 SDL_GetMouseState() returns the new position. It takes a mouse motion
257 event (generated here by SDL_WarpMouse()) for everything to work
260 (This is with SDL 1.2.12.)
262 if(prev
== SDL_DISABLE
&& enable
) {
264 SDL_GetMouseState(&x
, &y
);
269 /*! Opens the image \a filename as an SDL_Surface. Logs a warning if the file
271 \param filename The image to open (in any format supported by SDL_image).
272 \return The opened image (as an SDL_Surface) or NULL on error.
274 SDL_Surface
*load_image(const char *filename
) {
275 SDL_Surface
*intermediate
, *image
;
277 if(!filename
|| !*filename
) return 0;
279 intermediate
= IMG_Load(filename
);
282 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
283 "Can't open image \"%s\"", filename
);
287 image
= SDL_DisplayFormatAlpha(intermediate
);
288 SDL_FreeSurface(intermediate
);
290 increment_allocated(allocated_sdl_surface(image
));
295 int resize_screen(struct smode_t
*smode
, const SDL_ResizeEvent
*resize
) {
296 int x
= smode
->width
;
297 int y
= smode
->height
;
299 smode
->width
= resize
->w
;
300 smode
->height
= resize
->h
;
302 if(try_resizing_screen(smode
)) {
312 /*! Toggles fullscreen mode for the xuni window.
314 First tries SDL_WM_ToggleFullScreen(); if that fails, it then tries
315 try_resizing_screen().
317 \param smode The current screen mode that the xuni window is in.
318 \return Zero on success, nonzero on failure.
320 int toggle_fullscreen(struct smode_t
*smode
) {
321 smode
->fullscreen
= !smode
->fullscreen
;
325 if(SDL_WM_ToggleFullScreen(smode
->screen
)) return 0;
327 if(!try_resizing_screen(smode
)) return 0;
329 smode
->fullscreen
= !smode
->fullscreen
; /* undo previous toggling */
330 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
331 "Can't toggle fullscreen to \"%s\"",
332 smode
->fullscreen
? "fullscreen" : "windowed");
337 void use_screen_mode(struct xuni_t
*xuni
, int width
, int height
) {
340 event
.resize
.w
= width
;
341 event
.resize
.h
= height
;
343 if(!resize_screen(xuni
->smode
, &event
.resize
)) {
344 widget_event(xuni
, xuni
->gui
->widget
, WIDGET_EVENT_RESCALE
);
348 /*! Changes the title of the xuni window to the printf-compatible arguments
350 \param format The format string to change the title of the xuni window to.
351 \param ... The printf-compatible arguments matching format specifiers in
354 void set_caption(const char *format
, ...) {
358 va_start(arg
, format
);
359 wt
= malloc(BUFSIZ
+ 1);
361 vsprintf(wt
, format
, arg
);
363 SDL_WM_SetCaption(wt
, NULL
);
369 static Uint32
get_flags(const struct smode_t
*smode
) {
370 Uint32 flags
= SDL_SWSURFACE
| SDL_RESIZABLE
| SDL_ANYFORMAT
;
371 if(smode
->fullscreen
) flags
|= SDL_FULLSCREEN
;
376 static SDL_Surface
*set_graphics_mode(const struct smode_t
*smode
) {
377 return SDL_SetVideoMode(smode
->width
, smode
->height
, smode
->depth
,
381 SDL_Rect
**list_graphics_modes(struct smode_t
*smode
) {
382 return SDL_ListModes(NULL
, get_flags(smode
));
385 static int try_resizing_screen(struct smode_t
*smode
) {
386 SDL_Surface
*nscreen
= set_graphics_mode(smode
);
388 if(nscreen
) smode
->screen
= nscreen
;
393 /*! Locks the video surface so that data can be written directly to, pixel by
396 \param screen The surface to lock (should be the screen, the main video
399 void lock_screen(SDL_Surface
*screen
) {
400 if(SDL_MUSTLOCK(screen
)) {
401 if(SDL_LockSurface(screen
) < 0) {
402 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
,
403 __FILE__
, __LINE__
, "Can't lock screen (%p)", (void *)screen
);
408 /*! Reverts the effects of lock_screen(). That is, unlocks the video surface
409 so that it can be written to normally, with surface blitting and so on.
411 \param screen The surface to unlock (should be the screen, the main video
414 void unlock_screen(SDL_Surface
*screen
) {
415 if(SDL_MUSTLOCK(screen
)) {
416 SDL_UnlockSurface(screen
);
420 void clear_screen(SDL_Surface
*screen
) {
421 SDL_FillRect(screen
, NULL
, SDL_MapRGB(screen
->format
, 0, 0, 0));
424 SDL_Surface
*new_surface(int w
, int h
, int d
, int alpha
) {
425 SDL_Surface
*surface
= SDL_CreateRGBSurface(SDL_SWSURFACE
, w
, h
, d
,
426 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
427 0xff000000, /* red */
428 0x00ff0000, /* green */
429 0x0000ff00, /* blue */
432 0x000000ff, /* red */
433 0x0000ff00, /* green */
434 0x00ff0000, /* blue */
440 log_message(ERROR_TYPE_FATAL
, ERROR_FLAG_SDL
, __FILE__
, __LINE__
,
441 "Can't create new %ix%ix%i surface in %s edian", w
, h
, d
,
442 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
453 void free_surface(SDL_Surface
*surface
) {
454 decrement_allocated(allocated_sdl_surface(surface
));
456 if(/*xuni_memory_decrement*/(surface
)) SDL_FreeSurface(surface
);
459 static void call_blit_surface(SDL_Surface
*from
, SDL_Rect
*fromrect
,
460 SDL_Surface
*to
, SDL_Rect
*torect
) {
463 log_message(ERROR_TYPE_WARNING
, 0, __FILE__
, __LINE__
,
464 "Can't blit NULL surface: blitting %p to %p",
465 (void *)from
, (void *)to
);
469 if(SDL_BlitSurface(from
, fromrect
, to
, torect
) < 0) {
471 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
,
473 "Can't blit %lux%lu from (%lu,%lu) of surface %p (%lux%lu)",
474 (unsigned long)fromrect
->w
, (unsigned long)fromrect
->h
,
475 (unsigned long)fromrect
->x
, (unsigned long)fromrect
->y
,
476 (void *)from
, (unsigned long)from
->w
, (unsigned long)from
->h
);
479 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_SDL
, __FILE__
,
481 "Can't blit all (NULL rect) of surface %p (%lux%lu)",
482 (void *)from
, (unsigned long)from
->w
, (unsigned long)from
->h
);
486 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_CONTINUED
, __FILE__
,
488 "to surface %p (%lux%lu) at (%lu,%lu)",
489 (void *)to
, (unsigned long)to
->w
, (unsigned long)to
->h
,
490 (unsigned long)torect
->x
, (unsigned long)torect
->y
);
493 log_message(ERROR_TYPE_WARNING
, ERROR_FLAG_CONTINUED
, __FILE__
,
495 "to surface %p (%lux%lu) at (0,0) (NULL rect)",
496 (void *)to
, (unsigned long)to
->w
, (unsigned long)to
->h
);
501 void blit_surface(SDL_Surface
*screen
, SDL_Surface
*image
, int xp
, int yp
) {
509 call_blit_surface(image
, NULL
, screen
, &rect
);
512 void blit_surface_area(SDL_Surface
*screen
, SDL_Surface
*image
,
513 int tx
, int ty
, int fx
, int fy
, int fw
, int fh
) {
524 call_blit_surface(image
, &from
, screen
, &to
);
527 void blit_surface_repeat(SDL_Surface
*screen
, SDL_Surface
*image
,
528 int xp
, int yp
, int w
, int h
) {
531 SDL_Rect srect
, drect
;
536 for(x
= 0; x
< w
; x
+= image
->w
) {
537 for(y
= 0; y
< h
; y
+= image
->h
) {
541 if(x
+ image
->w
> w
) srect
.w
= w
- x
;
542 else srect
.w
= image
->w
;
543 if(y
+ image
->h
> h
) srect
.h
= h
- y
;
544 else srect
.h
= image
->h
;
546 call_blit_surface(image
, &srect
, screen
, &drect
);
552 /* !!! this function accounts for nearly 100% of xuni's execution time */
553 void blit_surface_repeat_area(SDL_Surface
*screen
, SDL_Surface
*image
,
554 int tx
, int ty
, int tw
, int th
, int fx
, int fy
, int fw
, int fh
) {
557 SDL_Rect srect
, drect
;
562 for(x
= 0; x
< tw
; x
+= fw
) {
563 for(y
= 0; y
< th
; y
+= fh
) {
567 if(x
+ fw
> tw
) srect
.w
= tw
- x
;
569 if(y
+ fh
> th
) srect
.h
= th
- y
;
572 call_blit_surface(image
, &srect
, screen
, &drect
);
577 void blit_surface_repeat_area(SDL_Surface
*screen
, SDL_Surface
*image
,
578 int tx
, int ty
, int tw
, int th
, int fx
, int fy
, int fw
, int fh
) {
581 SDL_Rect srect
, drect
;
588 for(x
= 0; x
< tw
; x
+= fw
) {
591 if(x
+ fw
> tw
) srect
.w
= tw
- x
;
595 for(y
= 0; y
< th
; y
+= fh
) {
598 if(y
+ fh
> th
) srect
.h
= th
- y
;
600 call_blit_surface(image
, &srect
, screen
, &drect
);
606 void blit_surface_fill_from(SDL_Surface
*screen
, SDL_Surface
*image
,
607 int tx
, int ty
, int tw
, int th
, int fx1
, int fy1
) {
610 SDL_Rect srect
, drect
;
612 /*x = image->w - fx1 < tw ? image->w - fx1 : tw;
613 y = image->h - fy1 < th ? image->h - fy1 : th;
614 blit_surface_area(screen, image,
615 tx, ty, fx1, fy1, x, y);*/
617 for(x
= fx1
- image
->w
; x
< tw
; x
+= image
->w
) {
618 for(y
= fy1
- image
->h
; y
< th
; y
+= image
->h
) {
622 srect
.w
= image
->w
- -x
;
628 if(x
+ image
->w
> tw
) srect
.w
= tw
- x
;
629 else srect
.w
= image
->w
;
635 srect
.h
= image
->h
- -y
;
641 if(y
+ image
->h
> th
) srect
.h
= th
- y
;
642 else srect
.h
= image
->h
;
645 call_blit_surface(image
, &srect
, screen
, &drect
);
650 void update_screen(struct xuni_t
*xuni
) {
651 SDL_UpdateRect(xuni
->smode
->screen
, 0, 0, 0, 0);
654 /*! Returns true if the position (\a xp, \a yp) is inside the rectangle
655 between (\a x, \a y) and (\c x+w, \c y+h).
656 \param xp The x coordinate of the position to check.
657 \param yp The y coordinate as the position to check.
658 \param x The x coordinate of the upper-left corner of the rectangle.
659 \param y The y coordinate of the upper-left corner of the rectangle.
660 \param w The width of the rectangle.
661 \param h The height of the rectangle.
662 \return True if the position is inside the rectangle.
664 int in_rect(int xp
, int yp
, int x
, int y
, int w
, int h
) {
665 return xp
>= x
&& xp
< x
+ w
&& yp
>= y
&& yp
< y
+ h
;
668 /*! Returns true if the position (\a xp, \a yp) is inside the rectangle
669 delimited by the SDL_Rect \a r.
670 \param xp The x-coordinate of the position to check.
671 \param yp The y-coordinate of the position to check.
672 \param r The rectangle to look for a position inside.
673 \return True if the position is inside the rectangle.
675 int in_sdl_rect(int xp
, int yp
, const SDL_Rect
*r
) {
676 return xp
>= r
->x
&& xp
< r
->x
+ r
->w
&& yp
>= r
->y
&& yp
< r
->y
+ r
->h
;
679 int pos_in_rect(int xp
, int yp
, const struct pos_t
*pos
) {
680 /*if(!pos->clip)*/ return in_sdl_rect(xp
, yp
, &pos
->real
);
687 printf("(%i,%i) in (%i+%i=%i,%i+%i=%i) by (%i,%i)\n",
689 pos
->real
.x
, pos
->clip
->xoff
, /*pos->clip->xclip,*/
690 pos
->real
.x
+ pos
->clip
->xoff
/*+ pos->clip->xclip*/,
691 pos
->real
.y
, pos
->clip
->yoff
, /*pos->clip->yclip,*/
692 pos
->real
.y
+ pos
->clip
->yoff
/*+ pos->clip->yclip*/,
693 pos
->clip
->wclip
, pos
->clip
->hclip
);
695 printf("origin values: (%i,%i) is in (%i,%i)\n",
696 xp
- pos
->real
.x
- pos
->clip
->xoff
,
697 yp
- pos
->real
.y
- pos
->clip
->yoff
,
698 pos
->clip
->wclip
, pos
->clip
->hclip
);
700 printf("(%i,%i) in (%i,%i) by (%i,%i)\n", xp
, yp
,
701 pos
->clip
->xoff
/*+ pos->clip->xclip*/,
702 pos
->clip
->yoff
/*+ pos->clip->yclip*/,
703 pos
->clip
->wclip
, pos
->clip
->hclip
);
706 printf(" is %i\n", in_rect(xp
, yp
,
707 pos
->clip
->xoff
/*+ pos->clip->xclip*/,
708 pos
->clip
->yoff
/*+ pos->clip->yclip*/,
709 pos
->clip
->wclip
, pos
->clip
->hclip
));
711 return in_rect(xp
, yp
,
712 pos
->clip
->xoff
/*+ pos->clip->xclip*/,
713 pos
->clip
->yoff
/*+ pos->clip->yclip*/,
714 pos
->clip
->wclip
, pos
->clip
->hclip
);
719 static struct widget_t
*widget_selable(struct widget_t
*widget
) {
720 if(!widget
) return 0;
722 /*printf("widget_selable(): \"%s\"\n", widget->name);*/
724 while(widget
->selable
&& widget
->base
) {
725 widget
= widget
->base
;
726 /*printf(" -> \"%s\"\n", widget->name);*/
733 static void fill_area_no_alpha(SDL_Surface
*screen
,
734 int x
, int y
, int w
, int h
, Uint8 r
, Uint8 g
, Uint8 b
) {
743 SDL_FillRect(screen
, &rect
, SDL_MapRGB(screen
->format
, r
, g
, b
));
746 void fill_area(SDL_Surface
*screen
, int x
, int y
, int w
, int h
,
747 SDL_Surface
*image
, int ix
, int iy
) {
751 SDL_GetRGBA(get_pixel(image
, ix
, iy
), image
->format
, &r
, &g
, &b
, &a
);
753 if(a
== 255) { /* opaque, can use fast filling */
754 fill_area_no_alpha(screen
, x
, y
, w
, h
, r
, g
, b
);
757 blit_surface_repeat_area(screen
, image
, x
, y
, w
, h
, ix
, iy
, 1, 1);
761 static Uint32
get_pixel(SDL_Surface
*surface
, int x
, int y
) {
764 if(!surface
->pixels
) return 0;
766 p
= (Uint8
*)surface
->pixels
768 + x
* surface
->format
->BytesPerPixel
;
770 if(x
< 0 || y
< 0 || x
>= surface
->w
|| y
>= surface
->h
) return 0;
772 switch(surface
->format
->BytesPerPixel
) {
778 if(SDL_BYTEORDER
== SDL_BIG_ENDIAN
) {
779 return p
[0] << 16 | p
[1] << 8 | p
[2];
781 else return p
[0] | p
[1] << 8 | p
[2] << 16;