add guncon support
[pcsx_rearmed.git] / frontend / plugin_lib.c
CommitLineData
b60f2812 1/*
8f892648 2 * (C) notaz, 2010-2011
b60f2812 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>
69af03a2 10#include <string.h>
11#include <stdarg.h>
799b0b87 12#include <stdint.h>
72228559 13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17#include <unistd.h>
7c0f51de 18#include <pthread.h>
b60f2812 19
72228559 20#include "plugin_lib.h"
b60f2812 21#include "linux/fbdev.h"
69af03a2 22#include "common/fonts.h"
23#include "common/input.h"
24#include "omap.h"
3c70c47b 25#include "menu.h"
8f892648 26#include "main.h"
72228559 27#include "pcnt.h"
4c08b9e7 28#include "pl_gun_ts.h"
69af03a2 29#include "../libpcsxcore/new_dynarec/new_dynarec.h"
799b0b87 30#include "../libpcsxcore/psemu_plugin_defs.h"
b60f2812 31
b60f2812 32void *pl_fbdev_buf;
bce6b056 33int pl_frame_interval;
4c08b9e7 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;
72228559 38static int pl_fbdev_w, pl_fbdev_h, pl_fbdev_bpp;
bce6b056 39static int flip_cnt, vsync_cnt, flips_per_sec, tick_per_sec;
40static float vsps_cur;
cefe86b7 41static int vsync_usec_time;
72228559 42
4c08b9e7 43
44static __attribute__((noinline)) int get_cpu_ticks(void)
72228559 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
8f892648 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
72228559 70static void print_fps(void)
71{
72 if (pl_fbdev_bpp == 16)
bce6b056 73 pl_text_out16(2, pl_fbdev_h - 10, "%2d %4.1f", flips_per_sec, vsps_cur);
72228559 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}
b60f2812 81
90f1d26c 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
297b3d63 111void *pl_fbdev_set_mode(int w, int h, int bpp)
69af03a2 112{
d352cde2 113 void *ret;
b60f2812 114
72228559 115 if (w == pl_fbdev_w && h == pl_fbdev_h && bpp == pl_fbdev_bpp)
7947ab55 116 return pl_fbdev_buf;
72228559 117
69af03a2 118 pl_fbdev_w = w;
72228559 119 pl_fbdev_h = h;
120 pl_fbdev_bpp = bpp;
d352cde2 121
122 vout_fbdev_clear(layer_fb);
69af03a2 123 ret = vout_fbdev_resize(layer_fb, w, h, bpp, 0, 0, 0, 0, 3);
d352cde2 124 if (ret == NULL)
69af03a2 125 fprintf(stderr, "failed to set mode\n");
d352cde2 126 else
127 pl_fbdev_buf = ret;
128
bd6267e6 129 menu_notify_mode_change(w, h, bpp);
3c70c47b 130
297b3d63 131 return pl_fbdev_buf;
69af03a2 132}
133
ffd0d743 134void *pl_fbdev_flip(void)
69af03a2 135{
72228559 136 flip_cnt++;
297b3d63 137
138 if (pl_fbdev_buf != NULL) {
90f1d26c 139 if (g_opts & OPT_SHOWSPU)
140 draw_active_chans();
141
8f892648 142 if (hud_msg[0] != 0)
143 print_hud();
144 else if (g_opts & OPT_SHOWFPS)
297b3d63 145 print_fps();
8f892648 146
297b3d63 147 if (g_opts & OPT_SHOWCPU)
148 print_cpu_usage();
149 }
72228559 150
69af03a2 151 // let's flip now
152 pl_fbdev_buf = vout_fbdev_flip(layer_fb);
ffd0d743 153 return pl_fbdev_buf;
b60f2812 154}
155
6d1a1ac2 156int pl_fbdev_open(void)
157{
cefe86b7 158 struct timeval now;
159
6d1a1ac2 160 pl_fbdev_buf = vout_fbdev_flip(layer_fb);
161 omap_enable_layer(1);
cefe86b7 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
6d1a1ac2 170 return 0;
171}
172
173void pl_fbdev_close(void)
b60f2812 174{
6d1a1ac2 175 omap_enable_layer(0);
b60f2812 176}
177
29a8c4f3 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
15d46930 187static void update_input(void)
188{
189 int actions[IN_BINDTYPE_COUNT] = { 0, };
8f892648 190 unsigned int emu_act;
15d46930 191
192 in_update(actions);
4c08b9e7 193 if (in_type1 == PSE_PAD_TYPE_ANALOGPAD)
799b0b87 194 in_update_analogs();
8f892648 195 emu_act = actions[IN_BINDTYPE_EMU];
4c08b9e7 196 in_state_gun = (emu_act & SACTION_GUN_MASK) >> SACTION_GUN_TRIGGER;
197
198 emu_act &= ~SACTION_GUN_MASK;
8f892648 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
799b0b87 207 in_keystate = actions[IN_BINDTYPE_PLAYER12];
447783f8 208
209#ifdef X11
4218954f 210 extern int x11_update_keys(unsigned int *action);
211 in_keystate |= x11_update_keys(&emu_act);
212 emu_set_action(emu_act);
447783f8 213#endif
15d46930 214}
215
4c08b9e7 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
bce6b056 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
72228559 238/* called on every vsync */
239void pl_frame_limit(void)
240{
bce6b056 241 static struct timeval tv_old, tv_expect;
7c0f51de 242 static int vsync_cnt_prev;
bce6b056 243 struct timeval now;
cefe86b7 244 int diff, usadj;
bce6b056 245
246 vsync_cnt++;
72228559 247
15d46930 248 /* doing input here because the pad is polled
249 * thousands of times per frame for some reason */
250 update_input();
251
72228559 252 pcnt_end(PCNT_ALL);
bce6b056 253 gettimeofday(&now, 0);
72228559 254
bce6b056 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)
7c0f51de 259 vsps_cur = 1000000.0f * (vsync_cnt - vsync_cnt_prev) / diff;
260 vsync_cnt_prev = vsync_cnt;
72228559 261 flips_per_sec = flip_cnt;
7c0f51de 262 flip_cnt = 0;
bce6b056 263 tv_old = now;
bd6267e6 264 if (g_opts & OPT_SHOWCPU)
265 tick_per_sec = get_cpu_ticks();
8f892648 266
267 if (hud_new_msg > 0) {
268 hud_new_msg--;
269 if (hud_new_msg == 0)
270 hud_msg[0] = 0;
271 }
72228559 272 }
273#ifdef PCNT
274 static int ya_vsync_count;
275 if (++ya_vsync_count == PCNT_FRAMES) {
bce6b056 276 pcnt_print(vsps_cur);
72228559 277 ya_vsync_count = 0;
278 }
279#endif
280
c89cd762 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;
cefe86b7 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;
c89cd762 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
e64dc4c5 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;
bce6b056 305 }
72228559 306
307 pcnt_start(PCNT_ALL);
308}
309
69af03a2 310static void pl_text_out16_(int x, int y, const char *text)
b60f2812 311{
69af03a2 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 }
b60f2812 332}
333
69af03a2 334void pl_text_out16(int x, int y, const char *texto, ...)
b60f2812 335{
69af03a2 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);
b60f2812 344}
345
201c21e2 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
e64dc4c5 354struct rearmed_cbs pl_rearmed_cbs = {
201c21e2 355 pl_get_layer_pos,
ffd0d743 356 pl_fbdev_open,
357 pl_fbdev_set_mode,
358 pl_fbdev_flip,
359 pl_fbdev_close,
201c21e2 360};
361
7c0f51de 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
799b0b87 369#ifndef NDEBUG
370 // don't interfere with debug
371 return NULL;
372#endif
7c0f51de 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
4c08b9e7 416void pl_init(void)
417{
418 ts = pl_gun_ts_init();
419}