1"======================================================================
2|
3|   SdlDisplay wrapper class for libsdl
4|
5|
6 ======================================================================"
7
8
9"======================================================================
10|
11| Copyright 2008 Free Software Foundation, Inc.
12| Written by Tony Garnock-Jones and Michael Bridgen.
13|
14| This file is part of the GNU Smalltalk class library.
15|
16| The GNU Smalltalk class library is free software; you can redistribute it
17| and/or modify it under the terms of the GNU Lesser General Public License
18| as published by the Free Software Foundation; either version 2.1, or (at
19| your option) any later version.
20|
21| The GNU Smalltalk class library is distributed in the hope that it will be
22| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
23| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
24| General Public License for more details.
25|
26| You should have received a copy of the GNU Lesser General Public License
27| along with the GNU Smalltalk class library; see the file COPYING.LIB.
28| If not, write to the Free Software Foundation, 59 Temple Place - Suite
29| 330, Boston, MA 02110-1301, USA.
30|
31 ======================================================================"
32
33
34Object subclass: SdlDisplayFormat [
35    | caption iconCaption extent resizable fullScreen |
36
37    flags [
38	<category: 'private'>
39	self fullScreen ifTrue: [ ^SdlVideo sdlFullScreen ].
40	self resizable ifTrue: [ ^SdlVideo sdlResizable ].
41	^0
42    ]
43
44    caption [
45        "Answer the caption of the window when it is not minimized."
46
47	<category: 'accessing'>
48	caption ifNil: [ caption := 'GNU Smalltalk' ].
49	^caption
50    ]
51
52    caption: aString [
53        "Set the caption of the window when it is not minimized."
54
55	<category: 'accessing'>
56	caption := aString.
57    ]
58
59    iconCaption [
60        "Answer the caption of the window when it is minimized."
61
62	<category: 'accessing'>
63	^iconCaption ifNil: [ caption ].
64    ]
65
66    iconCaption: aString [
67        "Set the caption of the window when it is minimized.  A value of nil
68	 means the caption will not change when the window is minimized."
69
70	<category: 'accessing'>
71	iconCaption := aString
72    ]
73
74    extent [
75	"Return the size of the window."
76
77	<category: 'accessing'>
78	extent ifNil: [ extent := 640 @ 480 ].
79	^extent
80    ]
81
82    extent: aPoint [
83	"Set the size of the window."
84
85	<category: 'accessing'>
86	extent := aPoint
87    ]
88
89    fullScreen [
90	"Answer whether the SDL surface will be full-screen."
91
92	<category: 'accessing'>
93	fullScreen ifNil: [ fullScreen := false ].
94	^fullScreen
95    ]
96
97    fullScreen: aBoolean [
98	"Set whether the SDL surface will be full-screen."
99
100	<category: 'accessing'>
101	fullScreen := aBoolean
102    ]
103
104    resizable [
105	"Answer whether the SDL surface will be resizable.  If it is, the
106	 program will have to send the #resize: method to the display when
107	 it gets a resize event (the default SdlEventHandler does this)."
108
109	<category: 'accessing'>
110	resizable ifNil: [ resizable := false ].
111	^resizable
112    ]
113
114    resizable: aBoolean [
115	"Set whether the SDL surface will be resizable.  If it is, the
116	 program will have to send the #resize: method to the display when
117	 it gets a resize event (the default SdlEventHandler does this)."
118
119	<category: 'accessing'>
120	resizable := aBoolean
121    ]
122]
123
124Object subclass: SdlDisplay [
125    <category: 'LibSDL-Wrapper'>
126    <comment: 'I provide an object-oriented wrapper for some SDL_video
127functions.  A Display can be connected to an EventSource and be used
128as the destination for a Cairo surface.'>
129
130    | surface flags extent caption iconCaption eventSource |
131
132    CurrentDisplay := nil.
133    DefaultFormat := nil.
134    SdlDisplay class >> current [
135	"Return the default display, creating one if none exists."
136
137	<category: 'accessing'>
138	"Creating the display will set CurrentDisplay too."
139	CurrentDisplay isNil ifTrue: [ ^self new ].
140	^CurrentDisplay
141    ]
142
143    SdlDisplay class >> current: aDisplay [
144	"Set the default display."
145
146	<category: 'accessing'>
147	CurrentDisplay := aDisplay
148    ]
149
150    SdlDisplay class >> initialize [
151        "Initialize the class, and initialize SDL when the library is loaded."
152
153        <category: 'initialization'>
154        ObjectMemory addDependent: self.
155        self sdlInit.
156    ]
157
158    SdlDisplay class >> update: aspect [
159        "Tie the event loop to image quit and restart."
160
161        <category: 'initialization'>
162        aspect == #returnFromSnapshot ifTrue: [ self sdlInit ].
163	self changed: aspect
164    ]
165
166    SdlDisplay class >> sdlInit [
167        "Initialize the SDL video subsystem, which is needed to get events."
168        Sdl sdlInit: (Sdl sdlInitVideo bitOr: Sdl sdlInitNoParachute).
169    ]
170
171    SdlDisplay class >> defaultFormat [
172	"Return the default format of the display, which is also the
173	 format used when #current is called and there is no default
174	 display."
175
176	<category: 'accessing'>
177	DefaultFormat isNil ifTrue: [ DefaultFormat := SdlDisplayFormat new ].
178	^ DefaultFormat
179    ]
180
181    SdlDisplay class >> defaultSize [
182	"Return the default size of the display, which is also the
183	 size used when #current is called and there is no default
184	 display."
185
186	<category: 'accessing'>
187	 ^ self defaultFormat extent
188    ]
189
190    SdlDisplay class >> defaultFormat: aDisplayFormat [
191	"Set the default format of the display."
192
193	<category: 'accessing'>
194	DefaultFormat := aDisplayFormat
195    ]
196
197    SdlDisplay class >> defaultSize: aPoint [
198	"Set the default size of the display."
199
200	<category: 'accessing'>
201	self defaultFormat extent: aPoint
202    ]
203
204    SdlDisplay class >> format: aSdlDisplayFormat [
205	"Return an SdlDisplay with the given format."
206
207	<category: 'instance creation'>
208	^self basicNew initialize: aSdlDisplayFormat
209    ]
210
211    SdlDisplay class >> extent: aPoint [
212	"Return an SdlDisplay with the given width and height."
213
214	<category: 'instance creation'>
215	^self format: (self defaultFormat copy extent: aPoint; yourself)
216    ]
217
218    SdlDisplay class >> new [
219	"Return an SdlDisplay with the default width and height."
220
221	<category: 'instance creation'>
222	^self format: self defaultFormat
223    ]
224
225    sdlSurface [
226	<category: 'private - accessing'>
227	^surface
228    ]
229
230    sdlSurface: anSdlSurface [
231	<category: 'private - accessing'>
232	surface := anSdlSurface
233    ]
234
235    mapRed: r green: g blue: b [
236	"Return an SDL color index for the given red/green/blue triplet."
237
238	<category: 'drawing-SDL'>
239	^ SdlVideo sdlMapRGB: surface format value r: r g: g b: b
240    ]
241
242    fillRect: aRect color: aColorNumber [
243	"Fill a rectangle in the display with the color whose index is in
244	 aColorNumber."
245
246	<category: 'drawing-SDL'>
247	| r |
248	r := SDL.SdlRect gcNew.
249	r x value: aRect left.
250	r y value: aRect top.
251	r w value: aRect width.
252	r h value: aRect height.
253	SdlVideo sdlFillRect: surface dstRect: r color: aColorNumber
254    ]
255
256    critical: aBlock [
257	"Execute aBlock while the surface is locked.  This must be
258	 called while drawing on the surface directly (e.g. via Cairo)"
259
260	<category: 'drawing-direct'>
261	(SdlVideo sdlLockSurface: surface) == 0 ifFalse: [
262	    self error: 'Could not lock surface ', surface].
263	^ aBlock ensure: [SdlVideo sdlUnlockSurface: surface]
264    ]
265
266    extent [
267	"Return the size of the display."
268	^ extent
269    ]
270
271    initialize: aFormat [
272	"Initialize the display by hooking it up to the SdlEventSource."
273
274	<category: 'initialization'>
275	caption := aFormat caption.
276	iconCaption := aFormat iconCaption.
277	extent := aFormat extent.
278	flags := aFormat flags.
279
280	self class addDependent: self.
281
282	"It's our first run - simulate returning from a saved image in
283	order to set up the display window etc."
284	CurrentDisplay isNil ifTrue: [ self class current: self ].
285	self create
286    ]
287
288    update: aspect [
289        "Tie the event loop to image quit and restart."
290
291        <category: 'initialization'>
292        aspect == #returnFromSnapshot ifTrue: [
293	    self create.
294            self eventSource handler isNil ifFalse: [ self eventSource startEventLoop ].
295            self changed: #returnFromSnapshot.
296            ^self].
297        aspect == #aboutToQuit ifTrue: [
298	    self shutdown.
299            self eventSource interruptEventLoop.
300            ^self].
301    ]
302
303    shutdown [
304	self sdlSurface: nil.
305    ]
306
307    eventSource [
308	"Return the EventSource associated to this display."
309	eventSource isNil ifTrue: [ eventSource := SdlEventSource new ].
310	^eventSource
311    ]
312
313    caption [
314	"Return the caption of the window when it is not minimized."
315
316	<category: 'accessing'>
317	^caption
318    ]
319
320    iconCaption [
321	"Return the caption of the window when it is minimized."
322
323	<category: 'accessing'>
324	^iconCaption
325    ]
326
327    caption: aString [
328	"Set the caption of the window when it is not minimized."
329
330	<category: 'accessing'>
331	caption := aString.
332	self setCaptions.
333    ]
334
335    iconCaption: aString [
336	"Set the caption of the window when it is minimized."
337
338	<category: 'accessing'>
339	iconCaption := aString.
340	self setCaptions.
341    ]
342
343    caption: aCaptionString iconCaption: anIconCaptionString [
344	"Set up the window to use aCaptionString as its caption when it is
345	 not minimized, and anIconCaptionString when it is."
346
347	<category: 'accessing'>
348	caption := aCaptionString.
349	iconCaption := anIconCaptionString.
350	self setCaptions.
351    ]
352
353    create [
354	"Private - Actually create the display.
355
356	TODO: add more accessors to match SDL flags (e.g. fullscreen, double
357	buffer, resizable, h/w surfaces)."
358
359	<category: 'initialization'>
360	| flags screen |
361	screen := SdlVideo sdlSetVideoMode: extent x height: extent y bpp: 32 flags: self flags.
362	self sdlSurface: screen.
363	self setCaptions.
364    ]
365
366    resize: newSize [
367	"Change the extent of the display to newSize."
368
369	<category: 'resize'>
370	self shutdown.
371	extent := newSize.
372	self create.
373	self changed: #resize
374    ]
375
376    flags [
377	"Private - Return the SDL_SetVideoMode flags."
378
379	<category: 'private'>
380	^flags " bitOr: SdlVideo sdlFullScreen."
381    ]
382
383    setCaptions [
384	"Private - sets captions from my instance variables."
385
386	<category: 'private'>
387	SdlVideo sdlWMSetCaption: self caption icon: self iconCaption.
388    ]
389
390    flip [
391	"Move the contents of the surface to the screen.  Optimized for
392	 double-buffered surfaces, but always works."
393
394	<category: 'drawing'>
395	SdlVideo sdlFlip: self sdlSurface.
396    ]
397
398    isGLDisplay [
399	"Return true if this is an OpenGL display and graphics should be
400	 performed using OpenGL calls."
401
402	<category: 'testing'>
403	^false
404    ]
405
406    updateRectangle: aRect [
407	"Move the contents of the given rectangle from the surface to the
408	 screen."
409
410	<category: 'drawing'>
411	| x y |
412        SdlVideo sdlUpdateRect: self sdlSurface
413                 x: (x := aRect left floor)
414                 y: (y := aRect top floor)
415                 w: aRect right ceiling - x
416                 h: aRect height ceiling - y.
417    ]
418
419    updateRectangles: upTo rects: sdlrects [
420	"Private - Move the contents of the given SdlRect objects from the
421	 surface to the screen."
422
423	<category: 'drawing-SDL'>
424	SdlVideo sdlUpdateRects: self sdlSurface
425		 numRects: upTo
426		 rects: sdlrects.
427    ]
428].
429
430Eval [
431    SdlDisplay initialize
432]
433