1// This file is part of SmallBASIC
2//
3// lowlevel device (OS) I/O
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#define DEVICE_MODULE
11
12#include "include/osd.h"
13#include "common/device.h"
14#include "common/messages.h"
15#include "common/sberr.h"
16#include "common/blib.h"
17
18#define W2X(x) (((((x) - dev_Wx1) * dev_Vdx) / dev_Wdx) + dev_Vx1)
19#define W2Y(y) (((((y) - dev_Wy1) * dev_Vdy) / dev_Wdy) + dev_Vy1)
20#define X2W(x) (((((x) - dev_Vx1) * dev_Wdx) / dev_Vdx) + dev_Wx1)
21#define Y2W(y) (((((y) - dev_Vy1) * dev_Wdy) / dev_Vdy) + dev_Wy1)
22#define W2D2(x,y) { (x) = W2X((x)); (y) = W2Y((y)); }
23#define W2D4(x1,y1,x2,y2) { W2D2((x1),(y1)); W2D2((x2),(y2)); }
24#define CLIPENCODE(x,y,c) { c = (x < dev_Vx1); \
25 c |= ((y < dev_Vy1) << 1); \
26 c |= ((x > dev_Vx2) << 2); \
27 c |= ((y > dev_Vy2) << 3); }
28#define CLIPIN(c) ((c & 0xF) == 0)
29
30uint32_t os_color_depth = 16;
31byte os_graphics = 0; // CONSOLE
32int os_graf_mx = 80;
33int os_graf_my = 25;
34
35// graphics - viewport
36int32_t dev_Vx1;
37int32_t dev_Vy1;
38int32_t dev_Vx2;
39int32_t dev_Vy2;
40int32_t dev_Vdx;
41int32_t dev_Vdy;
42
43// graphics - window world coordinates
44int32_t dev_Wx1;
45int32_t dev_Wy1;
46int32_t dev_Wx2;
47int32_t dev_Wy2;
48int32_t dev_Wdx;
49int32_t dev_Wdy;
50long dev_fgcolor = 0;
51long dev_bgcolor = 15;
52
53//
54// Returns data from pointing-device
55// (see PEN(x), osd_getpen(x))
56//
57int dev_getpen(int code) {
58 int result = 0;
59 if (os_graphics) {
60 result = osd_getpen(code);
61 switch (code) {
62 case 1:
63 case 4:
64 case 10:
65 result = X2W(result);
66 break;
67
68 case 2:
69 case 5:
70 case 11:
71 result = Y2W(result);
72 break;
73 }
74 }
75 return result;
76}
77
78//
79// enable/disable default pointing device (pen or mouse)
80//
81void dev_setpenmode(int enable) {
82 if (os_graphics) {
83 osd_setpenmode(enable);
84 }
85}
86
87//
88// returns the x position of cursor (in pixels)
89//
90int dev_getx() {
91 return osd_getx();
92}
93
94//
95// returns the y position of cursor (in pixels)
96//
97int dev_gety() {
98 return osd_gety();
99}
100
101//
102// sets the position of cursor
103// x,y are in pixels
104//
105void dev_setxy(int x, int y, int transform) {
106 if (x < 0 || x > os_graf_mx) {
107 return;
108 }
109 if (y < 0 || y > os_graf_my) {
110 return;
111 }
112
113 if (transform) {
114 x = W2X(x);
115 y = W2Y(y);
116 }
117
118 osd_setxy(x, y);
119}
120
121//
122// sets the currect foreground & background color
123// the background color is used only for texts
124//
125void dev_settextcolor(long fg, long bg) {
126 if (bg == -1) {
127 bg = dev_bgcolor;
128 }
129
130 if ((fg <= 15) && (bg <= 15) && (fg >= 0) && (bg >= 0)) { // VGA
131 if (bg != -1) {
132 dev_bgcolor = bg;
133 }
134 osd_settextcolor(dev_fgcolor = fg, dev_bgcolor);
135 } else {
136 osd_settextcolor((dev_fgcolor = fg), (dev_bgcolor = bg));
137 }
138}
139
140//
141// prints a string
142//
143void dev_print(const char *str) {
144 osd_write(str);
145}
146
147//
148// clears the screen
149//
150void dev_cls() {
151 graph_reset();
152 osd_cls();
153}
154
155//
156// returns the width of 'str' in pixels
157//
158int dev_textwidth(const char *str) {
159 if (os_graphics) {
160 return osd_textwidth(str);
161 }
162 // console
163 return strlen(str);
164}
165
166//
167// returns the height of 'str' in pixels
168//
169int dev_textheight(const char *str) {
170 if (os_graphics) {
171 return osd_textheight(str);
172 }
173 // console
174 return 1;
175}
176
177//
178// changes the current foreground color
179//
180void dev_setcolor(long color) {
181 if (color <= 15 && color >= 0) {
182 osd_setcolor(dev_fgcolor = color);
183 } else if (color < 0) {
184 osd_setcolor((dev_fgcolor = color));
185 }
186}
187
188//
189// draw a pixel
190//
191void dev_setpixel(int x, int y) {
192 x = W2X(x);
193 y = W2Y(y);
194 if (x >= dev_Vx1 && x <= dev_Vx2) {
195 if (y >= dev_Vy1 && y <= dev_Vy2) {
196 osd_setpixel(x, y);
197 }
198 }
199}
200
201//
202// returns the value of a pixel
203//
204long dev_getpixel(int x, int y) {
205 x = W2X(x);
206 y = W2Y(y);
207 if ((x >= dev_Vx1 && x <= dev_Vx2) &&
208 (y >= dev_Vy1 && y <= dev_Vy2)) {
209 return osd_getpixel(x, y);
210 }
211 return 0;
212}
213
214//
215// Cohen-Sutherland clipping
216//
217void dev_clipline(int *x1, int *y1, int *x2, int *y2, int *visible) {
218 int done, in1, in2, sw;
219 int c1, c2;
220
221 *visible = done = 0;
222 do {
223 CLIPENCODE(*x1, *y1, c1);
224 CLIPENCODE(*x2, *y2, c2);
225 in1 = CLIPIN(c1);
226 in2 = CLIPIN(c2);
227 if (in1 && in2) {
228 *visible = done = 1;
229 } else if ((c1 & c2 & 0x1) || (c1 & c2 & 0x2) || (c1 & c2 & 0x4) || (c1 & c2 & 0x8)) {
230 // visible = false
231 done = 1;
232 } else {
233 // at least one point is outside
234 if (in1) {
235 // swap
236 sw = *x1;
237 *x1 = *x2;
238 *x2 = sw;
239 sw = *y1;
240 *y1 = *y2;
241 *y2 = sw;
242 sw = c1;
243 c1 = c2;
244 c2 = sw;
245 }
246
247 if (*x1 == *x2) {
248 if (c1 & 0x2) {
249 *y1 = dev_Vy1;
250 } else {
251 *y1 = dev_Vy2;
252 }
253 } else {
254 if (c1 & 0x1) {
255 *y1 += (*y2 - *y1) * (dev_Vx1 - *x1) / (*x2 - *x1);
256 *x1 = dev_Vx1;
257 } else if (c1 & 0x4) {
258 *y1 += (*y2 - *y1) * (dev_Vx2 - *x1) / (*x2 - *x1);
259 *x1 = dev_Vx2;
260 } else if (c1 & 0x2) {
261 *x1 += (*x2 - *x1) * (dev_Vy1 - *y1) / (*y2 - *y1);
262 *y1 = dev_Vy1;
263 } else if (c1 & 0x8) {
264 *x1 += (*x2 - *x1) * (dev_Vy2 - *y1) / (*y2 - *y1);
265 *y1 = dev_Vy2;
266 }
267 }
268 }
269 } while (!done);
270}
271
272//
273// draw line
274//
275void dev_line(int x1, int y1, int x2, int y2) {
276 int visible;
277
278 W2D4(x1, y1, x2, y2);
279
280 // clip_line
281 dev_clipline(&x1, &y1, &x2, &y2, &visible);
282 if (visible) {
283 osd_line(x1, y1, x2, y2);
284 }
285}
286
287void dev_ellipse(int xc, int yc, int xr, int yr, double aspect, int fill) {
288 int windowXR = xr * dev_Vdx / dev_Wdx;
289 int windowYR = (yr * aspect) * dev_Vdx / dev_Wdx;
290 osd_ellipse(W2X(xc), W2Y(yc), windowXR, windowYR, fill);
291}
292
293void dev_arc(int xc, int yc, double r, double start, double end, double aspect) {
294 osd_arc(W2X(xc), W2Y(yc), r, start, end, aspect);
295}
296
297//
298// draw a line using foreground color
299//
300void osd_line(int x1, int y1, int x2, int y2);
301
302//
303// draw rectangle (filled or not)
304//
305void dev_rect(int x1, int y1, int x2, int y2, int fill) {
306 int c1, c2, in1, in2;
307 int px1 = x1;
308 int py1 = y1;
309 int px2 = x2;
310 int py2 = y2;
311
312 W2D4(x1, y1, x2, y2);
313
314 if (x1 == x2) {
315 dev_line(px1, py1, px2, py2);
316 return;
317 }
318 if (y1 == y2) {
319 dev_line(px1, py1, px2, py2);
320 return;
321 }
322
323 // check inside
324 CLIPENCODE(x1, y1, c1);
325 CLIPENCODE(x2, y2, c2);
326 in1 = CLIPIN(c1);
327 in2 = CLIPIN(c2);
328 if (in1 && in2) {
329 // its inside
330 osd_rect(x1, y1, x2, y2, fill);
331 } else {
332 // partial inside
333 int y;
334 if (fill) {
335 for (y = py1; y <= py2; y++) {
336 dev_line(px1, y, px2, y);
337 }
338 } else {
339 dev_line(px1, py1, px1, py2);
340 dev_line(px1, py2, px2, py2);
341 dev_line(px2, py2, px2, py1);
342 dev_line(px2, py1, px1, py1);
343 }
344 }
345}
346
347//
348// set viewport
349//
350void dev_viewport(int x1, int y1, int x2, int y2) {
351 if (x1 == x2 || y1 == y2) {
352 // reset
353 dev_Vx1 = 0;
354 dev_Vy1 = 0;
355 dev_Vx2 = os_graf_mx - 1;
356 dev_Vy2 = os_graf_my - 1;
357 dev_Vdx = os_graf_mx - 1;
358 dev_Vdy = os_graf_my - 1;
359 } else {
360 if ((x1 < 0) || (x2 < 0) || (y1 < 0) || (y2 < 0) || (x1 >= os_graf_mx) || (x2 >= os_graf_mx)
361 || (y1 >= os_graf_my) || (y2 >= os_graf_my)) {
362 rt_raise(ERR_VP_POS);
363 }
364
365 dev_Vx1 = x1;
366 dev_Vy1 = y1;
367 dev_Vx2 = x2;
368 dev_Vy2 = y2;
369 dev_Vdx = ABS(x2 - x1);
370 dev_Vdy = ABS(y2 - y1);
371
372 if (dev_Vdx == 0 || dev_Vdy == 0) {
373 rt_raise(ERR_VP_ZERO);
374 }
375 }
376
377 // reset window
378 dev_Wx1 = dev_Vx1;
379 dev_Wy1 = dev_Vy1;
380 dev_Wx2 = dev_Vx2;
381 dev_Wy2 = dev_Vy2;
382 dev_Wdx = dev_Vdx;
383 dev_Wdy = dev_Vdy;
384}
385
386//
387// set window
388//
389void dev_window(int x1, int y1, int x2, int y2) {
390 if (x1 == x2 || y1 == y2) {
391 // reset
392 dev_Wx1 = dev_Vx1;
393 dev_Wy1 = dev_Vy1;
394 dev_Wx2 = dev_Vx2;
395 dev_Wy2 = dev_Vy2;
396 dev_Wdx = dev_Vdx;
397 dev_Wdy = dev_Vdy;
398 } else {
399 dev_Wx1 = x1;
400 dev_Wy1 = y1;
401 dev_Wx2 = x2;
402 dev_Wy2 = y2;
403 dev_Wdx = x2 - x1;
404 dev_Wdy = y2 - y1;
405
406 if (dev_Wdx == 0 || dev_Wdy == 0) {
407 rt_raise(ERR_WIN_ZERO);
408 }
409 }
410}
411
412void dev_resize(int width, int height) {
413 if (dev_Vx2 == dev_Vdx && dev_Vx2 == os_graf_mx - 1) {
414 // viewport width is full-screen
415 if (dev_Wx2 == dev_Wdx && dev_Wx2 == dev_Vx2) {
416 // virtual window width is full-screen
417 dev_Wx2 = dev_Wdx = width - 1;
418 }
419 dev_Vx2 = dev_Vdx = width - 1;
420 }
421 if (dev_Vy2 == dev_Vdy && dev_Vy2 == os_graf_my - 1) {
422 // viewport height is full-screen
423 if (dev_Wy2 == dev_Wdy && dev_Wy2 == dev_Vy2) {
424 // virtual window height is full-screen
425 dev_Wy2 = dev_Wdy = height - 1;
426 }
427 dev_Vy2 = dev_Vdy = height - 1;
428 }
429 os_graf_mx = width;
430 os_graf_my = height;
431 setsysvar_int(SYSVAR_XMAX, width - 1);
432 setsysvar_int(SYSVAR_YMAX, height - 1);
433}
434
435void dev_map_point(int *x, int *y) {
436 *x = W2X(*x);
437 *y = W2Y(*y);
438}
439