add guncon support
[pcsx_rearmed.git] / frontend / plugin_lib.c
... / ...
CommitLineData
1/*
2 * (C) notaz, 2010-2011
3 *
4 * This work is licensed under the terms of the GNU GPLv2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdarg.h>
12#include <stdint.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17#include <unistd.h>
18#include <pthread.h>
19
20#include "plugin_lib.h"
21#include "linux/fbdev.h"
22#include "common/fonts.h"
23#include "common/input.h"
24#include "omap.h"
25#include "menu.h"
26#include "main.h"
27#include "pcnt.h"
28#include "pl_gun_ts.h"
29#include "../libpcsxcore/new_dynarec/new_dynarec.h"
30#include "../libpcsxcore/psemu_plugin_defs.h"
31
32void *pl_fbdev_buf;
33int pl_frame_interval;
34int in_type1, in_type2;
35int in_a1[2] = { 127, 127 }, in_a2[2] = { 127, 127 };
36int in_keystate, in_state_gun;
37static void *ts;
38static int pl_fbdev_w, pl_fbdev_h, pl_fbdev_bpp;
39static int flip_cnt, vsync_cnt, flips_per_sec, tick_per_sec;
40static float vsps_cur;
41static int vsync_usec_time;
42
43
44static __attribute__((noinline)) int get_cpu_ticks(void)
45{
46 static unsigned long last_utime;
47 static int fd;
48 unsigned long utime, ret;
49 char buf[128];
50
51 if (fd == 0)
52 fd = open("/proc/self/stat", O_RDONLY);
53 lseek(fd, 0, SEEK_SET);
54 buf[0] = 0;
55 read(fd, buf, sizeof(buf));
56 buf[sizeof(buf) - 1] = 0;
57
58 sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu", &utime);
59 ret = utime - last_utime;
60 last_utime = utime;
61 return ret;
62}
63
64static void print_hud(void)
65{
66 if (pl_fbdev_bpp == 16)
67 pl_text_out16(2, pl_fbdev_h - 10, "%s", hud_msg);
68}
69
70static void print_fps(void)
71{
72 if (pl_fbdev_bpp == 16)
73 pl_text_out16(2, pl_fbdev_h - 10, "%2d %4.1f", flips_per_sec, vsps_cur);
74}
75
76static void print_cpu_usage(void)
77{
78 if (pl_fbdev_bpp == 16)
79 pl_text_out16(pl_fbdev_w - 28, pl_fbdev_h - 10, "%3d", tick_per_sec);
80}
81
82// draw 192x8 status of 24 sound channels
83static __attribute__((noinline)) void draw_active_chans(void)
84{
85 extern void spu_get_debug_info(int *chans_out, int *fmod_chans_out, int *noise_chans_out); // hack
86 int live_chans, fmod_chans, noise_chans;
87
88 static const unsigned short colors[2] = { 0x1fe3, 0x0700 };
89 unsigned short *dest = (unsigned short *)pl_fbdev_buf +
90 pl_fbdev_w * (pl_fbdev_h - 10) + pl_fbdev_w / 2 - 192/2;
91 unsigned short *d, p;
92 int c, x, y;
93
94 if (pl_fbdev_bpp != 16)
95 return;
96
97 spu_get_debug_info(&live_chans, &fmod_chans, &noise_chans);
98
99 for (c = 0; c < 24; c++) {
100 d = dest + c * 8;
101 p = !(live_chans & (1<<c)) ? 0 :
102 (fmod_chans & (1<<c)) ? 0xf000 :
103 (noise_chans & (1<<c)) ? 0x001f :
104 colors[c & 1];
105 for (y = 0; y < 8; y++, d += pl_fbdev_w)
106 for (x = 0; x < 8; x++)
107 d[x] = p;
108 }
109}
110
111void *pl_fbdev_set_mode(int w, int h, int bpp)
112{
113 void *ret;
114
115 if (w == pl_fbdev_w && h == pl_fbdev_h && bpp == pl_fbdev_bpp)
116 return pl_fbdev_buf;
117
118 pl_fbdev_w = w;
119 pl_fbdev_h = h;
120 pl_fbdev_bpp = bpp;
121
122 vout_fbdev_clear(layer_fb);
123 ret = vout_fbdev_resize(layer_fb, w, h, bpp, 0, 0, 0, 0, 3);
124 if (ret == NULL)
125 fprintf(stderr, "failed to set mode\n");
126 else
127 pl_fbdev_buf = ret;
128
129 menu_notify_mode_change(w, h, bpp);
130
131 return pl_fbdev_buf;
132}
133
134void *pl_fbdev_flip(void)
135{
136 flip_cnt++;
137
138 if (pl_fbdev_buf != NULL) {
139 if (g_opts & OPT_SHOWSPU)
140 draw_active_chans();
141
142 if (hud_msg[0] != 0)
143 print_hud();
144 else if (g_opts & OPT_SHOWFPS)
145 print_fps();
146
147 if (g_opts & OPT_SHOWCPU)
148 print_cpu_usage();
149 }
150
151 // let's flip now
152 pl_fbdev_buf = vout_fbdev_flip(layer_fb);
153 return pl_fbdev_buf;
154}
155
156int pl_fbdev_open(void)
157{
158 struct timeval now;
159
160 pl_fbdev_buf = vout_fbdev_flip(layer_fb);
161 omap_enable_layer(1);
162
163 // try to align redraws to vsync
164 vout_fbdev_wait_vsync(layer_fb);
165 gettimeofday(&now, 0);
166 vsync_usec_time = now.tv_usec;
167 while (vsync_usec_time >= pl_frame_interval)
168 vsync_usec_time -= pl_frame_interval;
169
170 return 0;
171}
172
173void pl_fbdev_close(void)
174{
175 omap_enable_layer(0);
176}
177
178void *pl_prepare_screenshot(int *w, int *h, int *bpp)
179{
180 *w = pl_fbdev_w;
181 *h = pl_fbdev_h;
182 *bpp = pl_fbdev_bpp;
183
184 return pl_fbdev_buf;
185}
186
187static void update_input(void)
188{
189 int actions[IN_BINDTYPE_COUNT] = { 0, };
190 unsigned int emu_act;
191
192 in_update(actions);
193 if (in_type1 == PSE_PAD_TYPE_ANALOGPAD)
194 in_update_analogs();
195 emu_act = actions[IN_BINDTYPE_EMU];
196 in_state_gun = (emu_act & SACTION_GUN_MASK) >> SACTION_GUN_TRIGGER;
197
198 emu_act &= ~SACTION_GUN_MASK;
199 if (emu_act) {
200 int which = 0;
201 for (; !(emu_act & 1); emu_act >>= 1, which++)
202 ;
203 emu_act = which;
204 }
205 emu_set_action(emu_act);
206
207 in_keystate = actions[IN_BINDTYPE_PLAYER12];
208
209#ifdef X11
210 extern int x11_update_keys(unsigned int *action);
211 in_keystate |= x11_update_keys(&emu_act);
212 emu_set_action(emu_act);
213#endif
214}
215
216void pl_update_gun(int *xn, int *xres, int *y, int *in)
217{
218 if (ts)
219 pl_gun_ts_update(ts, xn, y, in);
220
221 *xres = pl_fbdev_w;
222 *y = *y * pl_fbdev_h >> 10;
223}
224
225#define MAX_LAG_FRAMES 3
226
227#define tvdiff(tv, tv_old) \
228 ((tv.tv_sec - tv_old.tv_sec) * 1000000 + tv.tv_usec - tv_old.tv_usec)
229// assumes us < 1000000
230#define tvadd(tv, us) { \
231 tv.tv_usec += us; \
232 if (tv.tv_usec >= 1000000) { \
233 tv.tv_usec -= 1000000; \
234 tv.tv_sec++; \
235 } \
236}
237
238/* called on every vsync */
239void pl_frame_limit(void)
240{
241 static struct timeval tv_old, tv_expect;
242 static int vsync_cnt_prev;
243 struct timeval now;
244 int diff, usadj;
245
246 vsync_cnt++;
247
248 /* doing input here because the pad is polled
249 * thousands of times per frame for some reason */
250 update_input();
251
252 pcnt_end(PCNT_ALL);
253 gettimeofday(&now, 0);
254
255 if (now.tv_sec != tv_old.tv_sec) {
256 diff = tvdiff(now, tv_old);
257 vsps_cur = 0.0f;
258 if (0 < diff && diff < 2000000)
259 vsps_cur = 1000000.0f * (vsync_cnt - vsync_cnt_prev) / diff;
260 vsync_cnt_prev = vsync_cnt;
261 flips_per_sec = flip_cnt;
262 flip_cnt = 0;
263 tv_old = now;
264 if (g_opts & OPT_SHOWCPU)
265 tick_per_sec = get_cpu_ticks();
266
267 if (hud_new_msg > 0) {
268 hud_new_msg--;
269 if (hud_new_msg == 0)
270 hud_msg[0] = 0;
271 }
272 }
273#ifdef PCNT
274 static int ya_vsync_count;
275 if (++ya_vsync_count == PCNT_FRAMES) {
276 pcnt_print(vsps_cur);
277 ya_vsync_count = 0;
278 }
279#endif
280
281 tvadd(tv_expect, pl_frame_interval);
282 diff = tvdiff(tv_expect, now);
283 if (diff > MAX_LAG_FRAMES * pl_frame_interval || diff < -MAX_LAG_FRAMES * pl_frame_interval) {
284 //printf("pl_frame_limit reset, diff=%d, iv %d\n", diff, pl_frame_interval);
285 tv_expect = now;
286 diff = 0;
287 // try to align with vsync
288 usadj = vsync_usec_time;
289 while (usadj < tv_expect.tv_usec - pl_frame_interval)
290 usadj += pl_frame_interval;
291 tv_expect.tv_usec = usadj;
292 }
293
294 if (!(g_opts & OPT_NO_FRAMELIM) && diff > pl_frame_interval) {
295 // yay for working usleep on pandora!
296 //printf("usleep %d\n", diff - pl_frame_interval / 2);
297 usleep(diff - pl_frame_interval / 2);
298 }
299
300 if (pl_rearmed_cbs.frameskip) {
301 if (diff < -pl_frame_interval)
302 pl_rearmed_cbs.fskip_advice = 1;
303 else if (diff >= 0)
304 pl_rearmed_cbs.fskip_advice = 0;
305 }
306
307 pcnt_start(PCNT_ALL);
308}
309
310static void pl_text_out16_(int x, int y, const char *text)
311{
312 int i, l, len = strlen(text), w = pl_fbdev_w;
313 unsigned short *screen = (unsigned short *)pl_fbdev_buf + x + y * w;
314 unsigned short val = 0xffff;
315
316 for (i = 0; i < len; i++, screen += 8)
317 {
318 for (l = 0; l < 8; l++)
319 {
320 unsigned char fd = fontdata8x8[text[i] * 8 + l];
321 unsigned short *s = screen + l * w;
322 if (fd&0x80) s[0] = val;
323 if (fd&0x40) s[1] = val;
324 if (fd&0x20) s[2] = val;
325 if (fd&0x10) s[3] = val;
326 if (fd&0x08) s[4] = val;
327 if (fd&0x04) s[5] = val;
328 if (fd&0x02) s[6] = val;
329 if (fd&0x01) s[7] = val;
330 }
331 }
332}
333
334void pl_text_out16(int x, int y, const char *texto, ...)
335{
336 va_list args;
337 char buffer[256];
338
339 va_start(args, texto);
340 vsnprintf(buffer, sizeof(buffer), texto, args);
341 va_end(args);
342
343 pl_text_out16_(x, y, buffer);
344}
345
346static void pl_get_layer_pos(int *x, int *y, int *w, int *h)
347{
348 *x = g_layer_x;
349 *y = g_layer_y;
350 *w = g_layer_w;
351 *h = g_layer_h;
352}
353
354struct rearmed_cbs pl_rearmed_cbs = {
355 pl_get_layer_pos,
356 pl_fbdev_open,
357 pl_fbdev_set_mode,
358 pl_fbdev_flip,
359 pl_fbdev_close,
360};
361
362/* watchdog */
363static void *watchdog_thread(void *unused)
364{
365 int vsync_cnt_old = 0;
366 int seen_dead = 0;
367 int sleep_time = 5;
368
369#ifndef NDEBUG
370 // don't interfere with debug
371 return NULL;
372#endif
373 while (1)
374 {
375 sleep(sleep_time);
376
377 if (stop) {
378 seen_dead = 0;
379 sleep_time = 5;
380 continue;
381 }
382 if (vsync_cnt != vsync_cnt_old) {
383 vsync_cnt_old = vsync_cnt;
384 seen_dead = 0;
385 sleep_time = 2;
386 continue;
387 }
388
389 seen_dead++;
390 sleep_time = 1;
391 if (seen_dead > 1)
392 fprintf(stderr, "watchdog: seen_dead %d\n", seen_dead);
393 if (seen_dead > 4) {
394 fprintf(stderr, "watchdog: lockup detected, aborting\n");
395 // we can't do any cleanup here really, the main thread is
396 // likely touching resources and would crash anyway
397 abort();
398 }
399 }
400}
401
402void pl_start_watchdog(void)
403{
404 pthread_attr_t attr;
405 pthread_t tid;
406 int ret;
407
408 pthread_attr_init(&attr);
409 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
410
411 ret = pthread_create(&tid, &attr, watchdog_thread, NULL);
412 if (ret != 0)
413 fprintf(stderr, "could not start watchdog: %d\n", ret);
414}
415
416void pl_init(void)
417{
418 ts = pl_gun_ts_init();
419}