egra: YesNoWindow cosmetix
[iv.d.git] / sdpy / core.d
blob39913d550b0970ff311e242533eeaa05152018ca
1 /*
2 * Pixel Graphics Library
3 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
4 * Understanding is not required. Only obedience.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License ONLY.
10 * This program 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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv.sdpy.core /*is aliced*/;
20 import iv.alice;
21 import iv.sdpy.compat;
22 private import iv.sdpy.gfxbuf;
25 // ////////////////////////////////////////////////////////////////////////// //
26 /// generic VideoLib exception
27 class VideoLibError : Exception {
28 static if (__VERSION__ > 2067) {
29 this (string msg, string file=__FILE__, usize line=__LINE__, Throwable next=null) @safe pure nothrow @nogc {
30 super(msg, file, line, next);
32 } else {
33 this (string msg, string file=__FILE__, usize line=__LINE__, Throwable next=null) @safe pure nothrow {
34 super(msg, file, line, next);
40 // ////////////////////////////////////////////////////////////////////////// //
41 /// output filter
42 enum VLFilter {
43 None,
44 Green,
45 BlackNWhite
49 /// is videolib initialized?
50 enum VLInitState {
51 Not, /// no
52 Partial, /// not fully (i.e. `vlInit()` failed in the middle)
53 Done /// completely initialized
57 // ////////////////////////////////////////////////////////////////////////// //
58 private shared VLInitState pvInited = VLInitState.Not;
60 /// is VideoLib properly initialized and videomode set?
61 @property VLInitState vlInitState () @trusted nothrow @nogc {
62 import core.atomic : atomicLoad;
63 return atomicLoad(pvInited);
66 // ////////////////////////////////////////////////////////////////////////// //
67 shared bool vlMag2x = true; // set to true to create double-sized window; default: true; have no effect after calling vlInit()
68 shared bool vlScanlines = true; // set to true to use 'scanline' filter in mag2x mode; default: true
69 shared VLFilter vlFilter = VLFilter.None; /// output filter; default: VLFilter.None
71 __gshared bool sdpyUseOpenGL = false;
72 __gshared bool sdpyShowFPS = true;
73 __gshared uint sdpyFPS = 35;
74 __gshared string sdpyWindowTitle = "SDPY";
77 // ////////////////////////////////////////////////////////////////////////// //
78 /// screen dimensions, should be changed prior to calling vlInit()
79 private shared uint vsWidth = 320;
80 private shared uint vsHeight = 240;
82 /// get current screen width
83 @gcc_inline @property uint vlWidth() () @trusted nothrow @nogc {
84 //static if (__VERSION__ > 2067) pragma(inline, true);
85 import core.atomic : atomicLoad;
86 return atomicLoad(vsWidth);
89 /// get current screen height
90 @gcc_inline @property uint vlHeight() () @trusted nothrow @nogc {
91 //static if (__VERSION__ > 2067) pragma(inline, true);
92 import core.atomic : atomicLoad;
93 return atomicLoad(vsHeight);
96 /// set screen width; must be used before vlInit()
97 @property void vlWidth (int wdt) @trusted {
98 import core.atomic : atomicLoad, atomicStore;
99 if (atomicLoad(pvInited) != VLInitState.Not) throw new VideoLibError("trying to change screen width after initialization");
100 if (wdt < 1 || wdt > 8192) throw new VideoLibError("invalid screen width");
101 atomicStore(vsWidth, wdt);
104 /// set screen height; must be used before vlInit()
105 @property void vlHeight (int hgt) @trusted {
106 import core.atomic : atomicLoad, atomicStore;
107 if (atomicLoad(pvInited) != VLInitState.Not) throw new VideoLibError("trying to change screen height after initialization");
108 if (hgt < 1 || hgt > 8192) throw new VideoLibError("invalid screen height");
109 atomicStore(vsHeight, hgt);
112 __gshared uint* vlVScr = null; /// current SDL 'virtual screen', ARGB format for LE
113 private __gshared uint* vscr2x = null; // this is used in magnifying blitters
115 /// build `vscr2x` if necessary, return buffer
116 @property uint[] vlBuildBuffer2Blit () @trusted nothrow @nogc { return vlPaintFrameDefault(); }
119 private __gshared int effectiveMag2x; // effective vlMag2x (1, 2)
120 private __gshared int prevLogSizeWas1x = 0; // DON'T change to bool!
122 @gcc_inline @property int vlEffectiveWidth() () nothrow @trusted @nogc {
123 //static if (__VERSION__ > 2067) pragma(inline, true);
124 import core.atomic : atomicLoad;
125 return cast(int)atomicLoad(vsWidth)*effectiveMag2x;
127 @gcc_inline @property int vlEffectiveHeight() () nothrow @trusted @nogc {
128 //static if (__VERSION__ > 2067) pragma(inline, true);
129 import core.atomic : atomicLoad;
130 return cast(int)atomicLoad(vsHeight)*effectiveMag2x;
132 @gcc_inline @property int vlEffectiveMag() () nothrow @trusted @nogc {
133 //static if (__VERSION__ > 2067) pragma(inline, true);
134 return effectiveMag2x;
138 // ////////////////////////////////////////////////////////////////////////// //
140 * Process CLI args from main().
142 * Params:
143 * args = command line arguments
145 * Returns:
146 * command line with processed arguments removed
148 void vlProcessArgs (ref string[] args) @trusted nothrow {
149 main_loop:
150 for (uint idx = 1; idx < args.length; ++idx) {
151 auto arg = args[idx];
152 if (arg == "--") break;
153 if (arg.length < 3 || arg[0] != '-' || arg[1] != '-') continue;
154 bool yes = true;
155 if (arg.length > 5 && arg[2..5] == "no-") {
156 yes = false;
157 arg = arg[5..$];
158 } else {
159 arg = arg[2..$];
161 switch (arg) {
162 case "tv": vlScanlines = yes; break;
163 case "bw": if (yes) vlFilter = VLFilter.BlackNWhite; break;
164 case "green": if (yes) vlFilter = VLFilter.Green; break;
165 case "1x": vlMag2x = !yes; break;
166 case "2x": vlMag2x = yes; break;
167 case "x11": sdpyUseOpenGL = !yes; break;
168 case "ogl": sdpyUseOpenGL = yes; break;
169 case "fps": sdpyShowFPS = yes; break;
170 case "vhelp":
171 if (yes) {
172 import core.stdc.stdlib : exit;
173 import core.stdc.stdio : stderr, fprintf;
174 fprintf(stderr,
175 "video options (add \"no-\" to negate):\n"~
176 " --tv scanlines filter\n"~
177 " --bw black-and-white filter\n"~
178 " --green green filter\n"~
179 " --1x normal size\n"~
180 " --2x magnify\n"~
181 " --x11 use X11 backend (default)\n"~
182 " --ogl use OpenGl backend\n"~
183 " --fps show FPS (default)\n"~
184 " --no-fps don't show FPS\n"
186 exit(0);
188 break;
189 default: continue main_loop;
191 // remove option
192 foreach (immutable c; idx..args.length-1) args[c] = args[c+1];
193 args.length -= 1;
194 --idx; // compensate for removed element
200 * Initialize buffers.
202 * Returns:
203 * nothing
205 * Throws:
206 * VideoLibError on error
208 void vlInit () @trusted {
209 import core.atomic : atomicLoad, atomicStore;
211 final switch (atomicLoad(pvInited)) with (VLInitState) {
212 case Not: break;
213 case Partial: throw new VideoLibError("can't continue initialization");
214 case Done: return;
217 if (vsWidth < 1 || vsHeight < 1) throw new VideoLibError("sdpy: screen size is not set");
219 import core.exception : onOutOfMemoryError;
220 import core.stdc.stdlib : malloc, free;
222 vlDeinitInternal();
224 effectiveMag2x = (vlMag2x ? 2 : 1);
225 prevLogSizeWas1x = 1;
227 if (vlVScr !is null) free(vlVScr);
228 vlVScr = cast(uint*)malloc(vsWidth*vsHeight*vlVScr[0].sizeof);
229 if (vlVScr is null) onOutOfMemoryError();
231 if (vscr2x !is null) free(vscr2x);
232 vscr2x = cast(uint*)malloc(vsWidth*effectiveMag2x*vsHeight*effectiveMag2x*vscr2x[0].sizeof);
233 if (vscr2x is null) onOutOfMemoryError();
235 atomicStore(pvInited, VLInitState.Partial);
236 GfxBuf.updateVScr();
237 atomicStore(pvInited, VLInitState.Done);
242 * Deinitialize, free resources.
244 * Params:
245 * none
247 * Returns:
248 * nothing
250 private void vlDeinitInternal () /*@trusted nothrow @nogc*/ {
251 import core.atomic : atomicLoad, atomicStore;
252 import core.stdc.stdlib : free;
254 if (atomicLoad(pvInited) == VLInitState.Not) return;
256 if (vlVScr !is null) { free(vlVScr); vlVScr = null; }
257 if (vscr2x !is null) { free(vscr2x); vscr2x = null; }
258 GfxBuf.updateVScr();
260 atomicStore(pvInited, VLInitState.Not);
265 * Shutdown, free resources. You don't need to call this explicitely.
267 * Params:
268 * none
270 * Returns:
271 * nothing
273 void vlDeinit () /*@trusted nothrow @nogc*/ {
274 import core.atomic : atomicLoad;
275 if (atomicLoad(pvInited) != VLInitState.Not) {
276 vlDeinitInternal();
281 // ////////////////////////////////////////////////////////////////////////// //
282 private enum buildBlit1x(string name, string op, string wrt) =
283 `private void `~name~` () @trusted nothrow @nogc {`~
284 ` auto s = cast(const(ubyte)*)vlVScr;`~
285 ` auto d = cast(ubyte*)vscr2x;`~
286 ` foreach (immutable _; 0..vsWidth*vsHeight) {`~
287 ` ubyte i = `~op~`;`~
288 ` `~wrt~`;`~
289 ` s += 4;`~
290 ` d += 4;`~
291 ` }`~
292 `}`;
295 mixin(buildBlit1x!("blit1xBW", "(s[0]*28+s[1]*151+s[2]*77)/256", "d[0] = d[1] = d[2] = i"));
296 mixin(buildBlit1x!("blit1xGreen", "(s[0]*28+s[1]*151+s[2]*77)/256", "d[0] = d[2] = 0; d[1] = i"));
299 private enum buildBlit2x(string name, string op) =
300 `private void `~name~` () @trusted nothrow @nogc {`~
301 ` auto s = cast(const(ubyte)*)vlVScr;`~
302 ` auto d = cast(uint*)vscr2x;`~
303 ` immutable auto wdt = vsWidth;`~
304 ` immutable auto wdt2x = vsWidth*2;`~
305 ` foreach (immutable y; 0..vsHeight) {`~
306 ` foreach (immutable x; 0..wdt) {`~
308 ` immutable uint c1 = ((((c0&0x00ff00ff)*6)>>3)&0x00ff00ff)|(((c0&0x0000ff00)*6)>>3)&0x0000ff00;`~
309 ` d[0] = d[1] = c0;`~
310 ` d[wdt2x] = d[wdt2x+1] = c1;`~
311 ` s += 4;`~
312 ` d += 2;`~
313 ` }`~
314 // fix d: skip one scanline
315 ` d += wdt2x;`~
316 ` }`~
317 `}`;
320 mixin(buildBlit2x!("blit2xTV", "immutable uint c0 = (cast(immutable(uint)*)s)[0];"));
321 mixin(buildBlit2x!("blit2xTVBW", "immutable ubyte i = cast(ubyte)((s[0]*28+s[1]*151+s[2]*77)/256); immutable uint c0 = (i<<16)|(i<<8)|i;"));
322 mixin(buildBlit2x!("blit2xTVGreen", "immutable ubyte i = cast(ubyte)((s[0]*28+s[1]*151+s[2]*77)/256); immutable uint c0 = i<<8;"));
325 // ////////////////////////////////////////////////////////////////////////// //
327 * Paint virtual screen onto blit buffer.
329 * Params:
330 * none
332 * Returns:
333 * nothing
335 private uint[] vlPaintFrameDefault () @trusted nothrow @nogc {
336 import core.atomic : atomicLoad;
337 // fix 'logical size'
338 immutable flt = atomicLoad(vlFilter);
339 immutable scanln = atomicLoad(vlScanlines);
340 if (effectiveMag2x == 2 && scanln) {
341 // mag2x and scanlines: size is 2x
342 if (prevLogSizeWas1x) {
343 prevLogSizeWas1x = 0;
345 } else {
346 // any other case: size is 2x
347 if (!prevLogSizeWas1x) {
348 prevLogSizeWas1x = 1;
351 // apply filters if any
352 if (effectiveMag2x == 2 && scanln) {
353 // heavy case: scanline filter turned on
354 final switch (flt) with (VLFilter) {
355 case None: blit2xTV(); break;
356 case BlackNWhite: blit2xTVBW(); break;
357 case Green: blit2xTVGreen(); break;
359 return vscr2x[0..(vsHeight*2)*(vsWidth*2)*vscr2x[0].sizeof];
360 } else {
361 // light cases
362 if (flt == VLFilter.None) {
363 // easiest case
364 return vlVScr[0..vsHeight*vsWidth*vlVScr[0].sizeof];
365 } else {
366 import core.stdc.string : memcpy;
367 final switch (flt) with (VLFilter) {
368 case None: memcpy(vscr2x, vlVScr, vsWidth*vsHeight*vlVScr[0].sizeof); break; // just in case
369 case BlackNWhite: blit1xBW(); break;
370 case Green: blit1xGreen(); break;
372 return vscr2x[0..vsHeight*vsWidth*vscr2x[0].sizeof];
378 // ////////////////////////////////////////////////////////////////////////// //
379 shared static ~this () {
380 vlDeinit();