bsnes v0.041 released

Archived bsnes development news, feature requests and bug reports. Forum is now located at http://board.byuu.org/
Locked
byuu

Post by byuu »

Meh, window is appearing on XP when placed at -1,-1. Changed that to 2560,1600 to stop that initial flicker before the windows appear centered. Also from the WIP I made it use showNormal() so it'll show windows even if they were previously minimized.
Some people prefer baby seal meat.
I'm still not following your point. I've already added it. It took me a few days but it's done. Everything that worked before is exactly the same, but you now have the option of doing more. Binary size is the same, source code grew by ~2k (mostly due to extreme commenting.)

Not necessary, no; but I wanted it. Now we have it, and it's one more bit of polish (along with infinite cheat codes, infinite length descriptions in UTF-8, cheat code grouping and sorting, UPS support, single or multi user modes, resizable config windows, flexible theming and backdrop images, ...) that no other SNES emulator has yet :D

And tons of stuff I'm missing: savestates, rewind, SuperFX, SA-1, speed, macros / key combos, movies, netplay, ...

-----

This works well enough for Windows:

Code: Select all

class Application : public QApplication {
public:
  #ifdef _WIN32
  bool winEventFilter(MSG *msg, long *result) {
    if(msg->message == WM_SYSCOMMAND) {
      if(msg->wParam == SC_SCREENSAVE || msg->wParam == SC_MONITORPOWER) {
        printf("blocked sleep\n");
        *result = 0;
        return true;
      }
    }

    return false;
  }
  #endif

  Application(int argc, char **argv) : QApplication(argc, argv) {}
};
Add XTestFakeKeyEvent sans XSync (to avoid X-Video stuttering issues per BearOso) and screensaver disable should be taken care of -- at least until someone works on the OS X port.
AamirM
Regen Developer
Regen Developer
Posts: 533
Joined: Sun Feb 17, 2008 8:01 am
Contact:

Post by AamirM »

byuu wrote:This works well enough for Windows:
Umm....should just this suffice on Windows?

Code: Select all

SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, SPIF_SENDCHANGE);
FitzRoy
Veteran
Posts: 861
Joined: Wed Aug 04, 2004 5:43 pm
Location: Sloop

Post by FitzRoy »

You redesigned your site again and I can't access the WIP page anymore. It sounds like it would be a hassle for most people, though.
that no other SNES emulator has yet :D
That might be because it's kind of useless (for this system), though. They allow the best translations, digital joypad for digital joypad and mouse for mouse.
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

FitzRoy wrote:Some people prefer baby seal meat.
Hey, it's pretty tasty and melts on the tongue. A real delicacy.
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
FirebrandX
Trooper
Posts: 376
Joined: Tue Apr 19, 2005 11:08 pm
Location: DFW area, TX USA
Contact:

Post by FirebrandX »

byuu wrote:(...flexible theming and backdrop images, ...)
Quickly figure out the code needed for my request on this:

A favorites database the user can make where the proper theme image is loaded with the game. :D
NES NTSC palette file:

http://www.firebrandx.com/downloads/fbx2pal.zip
byuu

Post by byuu »

So how do you guys feel about bsnes requiring WinXP or newer (instead of Win2k or newer)?

If we do that, I can use RawInput without all the GetProcAddress bullshit. That'll allow independent mapping of multiple keyboards and mice. Really only super important for playable dual-Justifier support.

It would also allow support of pretty much any USB HID device, including barcode scanners and things like that. Not that I'd be all that interested in supporting said devices ... but you never know.
A favorites database the user can make where the proper theme image is loaded with the game.
I wasn't really planning to do anything with that feature, but who knows for the future. Maybe just make it look for gamename.sfc + gamename.png and set that as the backdrop, if it exists. Assuming that's not too tough. Not promising I'll get around to it though ...
creaothceann
Seen it all
Posts: 2302
Joined: Mon Jan 03, 2005 5:04 pm
Location: Germany
Contact:

Post by creaothceann »

byuu wrote:So how do you guys feel about bsnes requiring WinXP or newer (instead of Win2k or newer)?
I switched from 98 to XP, so no problem for me.
vSNES | Delphi 10 BPLs
bsnes launcher with recent files list
DeleteMyAccountPlease
Rookie
Posts: 20
Joined: Mon Jan 24, 2005 12:45 am

Post by DeleteMyAccountPlease »

I am in agreement. WinXP+
FirebrandX
Trooper
Posts: 376
Joined: Tue Apr 19, 2005 11:08 pm
Location: DFW area, TX USA
Contact:

Post by FirebrandX »

byuu wrote:
A favorites database the user can make where the proper theme image is loaded with the game.
I wasn't really planning to do anything with that feature, but who knows for the future. Maybe just make it look for gamename.sfc + gamename.png and set that as the backdrop, if it exists. Assuming that's not too tough. Not promising I'll get around to it though ...
That would be perfect if you can do that. My Chrono Trigger them image worked great while playing through it, but I want to make themes for each of the popular games like Metroid & Zelda.
NES NTSC palette file:

http://www.firebrandx.com/downloads/fbx2pal.zip
Tallgeese
Justice is Blind
Posts: 620
Joined: Wed Jul 28, 2004 3:33 pm
Location: Test
Contact:

Post by Tallgeese »

byuu wrote:
P.S. What controller are you using that has so many buttons and axis?
http://www.amazon.co.uk/Thrustmaster-Tr ... B000U7RJ0W

What you don't see is L1/L2/R1/R2 and pressure-sensitive L3/R3. It does force-feedback and cost me $20 new.
Hoiw do you hold that thing!?

I try to use the back triggers but the edges nip at my fingers and annoy the crap out of me. Plus that tiny is 'tiny'.

I wish they made more like the Thrustmaster Firestorm Analog 3. Wish mine didn't break.

The presence of the two big back buttons ala N64 Z button really endeared that pad to me. I want more like it that don't kill my hands.

Also... I have problems believing that many still needing to use Windows 2000 have the computer specs to even run BSNES.
Last edited by Tallgeese on Wed Mar 25, 2009 1:09 am, edited 2 times in total.
I.S.T.
Zealot
Posts: 1325
Joined: Tue Nov 27, 2007 7:03 am

Post by I.S.T. »

Whaaaaaaaaaaaa? that thing looks painful to hold.
Tallgeese
Justice is Blind
Posts: 620
Joined: Wed Jul 28, 2004 3:33 pm
Location: Test
Contact:

Post by Tallgeese »

The one byuu posted, or the big bulky one I mentioned?

Which is this:

http://www.amazon.com/Thrustmaster-Fire ... B00008DWV5
I.S.T.
Zealot
Posts: 1325
Joined: Tue Nov 27, 2007 7:03 am

Post by I.S.T. »

I meant his, though that one looks uncomfortable too.
kode54
Zealot
Posts: 1140
Joined: Wed Jul 28, 2004 3:31 am
Contact:

Post by kode54 »

byuu wrote:Meh, window is appearing on XP when placed at -1,-1. Changed that to 2560,1600 to stop that initial flicker before the windows appear centered.
Yeah, negative coordinates are how a window is placed off the top or left of the primary monitor. Also, those coordinates could be on screen with some huge multi-monitor setup, so it might be a good idea to check GetSystemMetrics() for SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN as well as SM_XVIRTUALSCREEN and SM_YVIRTUALSCREEN. The first two retrieve the size of the total desktop area, while the latter two return the coordinates of the top left of the desktop. With the primary monitor being the top left, they would be zero, but if not, they would be negative numbers, so best to add them to the width/height for the lowest coordinates which would be off-screen. Whew.
bobthebuilder
Hazed
Posts: 76
Joined: Sat Jan 28, 2006 7:21 am

Post by bobthebuilder »

byuu wrote:So how do you guys feel about bsnes requiring WinXP or newer (instead of Win2k or newer)?
That is a good idea. 8)
adventure_of_link
Locksmith of Hyrule
Posts: 3634
Joined: Sun Aug 08, 2004 7:49 am
Location: 255.255.255.255
Contact:

Post by adventure_of_link »

no problems here making bsnes XP+.
<Nach> so why don't the two of you get your own room and leave us alone with this stupidity of yours?
NSRT here.
I.S.T.
Zealot
Posts: 1325
Joined: Tue Nov 27, 2007 7:03 am

Post by I.S.T. »

Deathlike2 will get you for this!
byuu

Post by byuu »

Okay, this is my first time writing a multi-threaded app, so let's see if I've done this correctly.

This is a simple app that spawns a thread to monitor WM_INPUT messages to an invisible window. It caches each message so that the main thread can periodically check to see how much the mouse has moved since the last check.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

#define _WIN32_WINNT 0x0501
#include <windows.h>

HANDLE RawInputMutex;
DWORD WINAPI RawInputThreadProc(void*);
LRESULT CALLBACK RawInputWindowProc(HWND, UINT, WPARAM, LPARAM);

class RawInput {
public:
  HWND hwnd;
  signed mouseMoveX;
  signed mouseMoveY;

  void init() {
    WNDCLASS wc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hInstance = GetModuleHandle(0);
    wc.lpfnWndProc = RawInputWindowProc;
    wc.lpszClassName = "rawinputclass";
    wc.lpszMenuName = 0;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);

    hwnd = CreateWindow("rawinputclass", "rawinput", WS_POPUP,
      64, 64, 320, 240,
      0, 0, GetModuleHandle(0), 0);

    RAWINPUTDEVICELIST *list;
    unsigned devices = 0;
    GetRawInputDeviceList(NULL, &devices, sizeof(RAWINPUTDEVICELIST));
    list = (RAWINPUTDEVICELIST*)malloc(sizeof(RAWINPUTDEVICELIST) * devices);
    int n = GetRawInputDeviceList(list, &devices, sizeof(RAWINPUTDEVICELIST));

    for(unsigned i = 0; i < devices; i++) {
      unsigned size = 512;
      char buffer[512];
      GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, buffer, &size);
      printf("Device '%s':\n", buffer);

      RID_DEVICE_INFO info;
      info.cbSize = sizeof(RID_DEVICE_INFO);

      size = info.cbSize;
      GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICEINFO, &info, &size);

      if(info.dwType == RIM_TYPEMOUSE) {
        printf("Mouse %p\n", list[i].hDevice);
        printf("ID = %d\n", info.mouse.dwId);
        printf("Buttons = %d\n", info.mouse.dwNumberOfButtons);
        printf("DPI = %d\n", info.mouse.dwSampleRate);
      } else if(info.dwType == RIM_TYPEKEYBOARD) {
        printf("Keyboard\n");
        printf("Type = %d\n", info.keyboard.dwType);
        printf("Subtype = %d\n", info.keyboard.dwSubType);
        printf("Mode = %d\n", info.keyboard.dwKeyboardMode);
        printf("Function keys = %d\n", info.keyboard.dwNumberOfFunctionKeys);
        printf("Indicators = %d\n", info.keyboard.dwNumberOfIndicators);
        printf("Keys total = %d\n", info.keyboard.dwNumberOfKeysTotal);
      }

      printf("\n");
    }

    RAWINPUTDEVICE rid[2];

    rid[0].usUsagePage = 1;
    rid[0].usUsage = 2;
    rid[0].dwFlags = RIDEV_INPUTSINK;
    rid[0].hwndTarget = hwnd;

    rid[1].usUsagePage = 1;
    rid[1].usUsage = 6;
    rid[1].dwFlags = RIDEV_INPUTSINK;
    rid[1].hwndTarget = hwnd;

    RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));
  }

  LRESULT wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    if(msg == WM_INPUT) {
      unsigned size = 0;
      GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
      RAWINPUT *input = (RAWINPUT*)malloc(size);
      GetRawInputData((HRAWINPUT)lparam, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));

      if(input->header.dwType == RIM_TYPEMOUSE) {
        WaitForSingleObject(RawInputMutex, INFINITE);
        mouseMoveX += input->data.mouse.lLastX;
        mouseMoveY += input->data.mouse.lLastY;
        ReleaseMutex(RawInputMutex);
      //input->header.hDevice
      //input->data.mouse.usButtonFlags
      } else if(input->header.dwType == RIM_TYPEKEYBOARD) {
      //input->data.keyboard.MakeCode
      //input->data.keyboard.Flags
      //input->data.keyboard.Message
      }

      free(input);
    }

    return DefWindowProc(hwnd, msg, wparam, lparam);
  }

  int main() {
    init();

    while(true) {
      MSG msg;
      GetMessage(&msg, hwnd, 0, 0);
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return 0;
  }

  signed mouseX() {
    WaitForSingleObject(RawInputMutex, INFINITE);
    signed result = mouseMoveX;
    mouseMoveX = 0;
    ReleaseMutex(RawInputMutex);
    return result;
  }

  signed mouseY() {
    WaitForSingleObject(RawInputMutex, INFINITE);
    signed result = mouseMoveY;
    mouseMoveY = 0;
    ReleaseMutex(RawInputMutex);
    return result;
  }

  RawInput() : mouseMoveX(0), mouseMoveY(0) {
  }
} rawinput;

DWORD WINAPI RawInputThreadProc(void *param) {
  return rawinput.main();
}

LRESULT CALLBACK RawInputWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
  return rawinput.wndproc(hwnd, msg, wparam, lparam);
}

int main() {
  RawInputMutex = CreateMutex(NULL, FALSE, NULL);
  CreateThread(NULL, 0, RawInputThreadProc, 0, 0, NULL);

  while(true) {
    signed x = rawinput.mouseX(), y = rawinput.mouseY();
    if(x || y) printf("%5d, %5d\n", x, y);
    Sleep(20);
  }

  return 0;
}
Mutexes seem fairly slow, but not horribly so. I can WaitForSingleObject + ReleaseMutex about ~1.1 million times a second, so ~0.9ns per. Should be more than enough for an input driver, at least.
_willow_
Hazed
Posts: 51
Joined: Mon Dec 24, 2007 2:03 am
Location: Russia
Contact:

Post by _willow_ »

Oh no byuu, don't be paranoid! There is no need for mutexes.
You may use more gentle approach:

Code: Select all

LRESULT wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
..
        //Atomic: adjust the movement accumulator.
        mouseMoveX += input->data.mouse.lLastX;
        //Atomic: adjust the movement accumulator.
        mouseMoveY += input->data.mouse.lLastY;
..
}


signed mouseX()
{
  //Atomic: get the result.
  register signed result = mouseMoveX;
  //Atomic: recalibrate the accumulator. (redefine the movement accumulator for new "zero" point)
  mouseMoveX -= result;
  return result;
}

signed mouseY()
{
  //Atomic: get the result.
  register signed result = mouseMoveY;
  //Atomic: recalibrate the accumulator. (redefine the movement accumulator for new "zero" point)
  mouseMoveY -= result;
  return result;
}
wndproc:
mouseMoveX += input->data.mouse.lLastX;
+= here is the atomic operation and do not require mutexes.

mouseX(), mouseY():
register signed result = mouseMoveX;
= here is the atomic operation too, so this code is iron safe, your mutex subroutine will die faster then single atomic ADD or MOV machine instructions.

Mutexes are the replacement for atomic operations, but there is TWO of them here. "mouseMove" and "result" are both ether basic integers or even registers


EDIT:

//Atomic: get the result.
register signed result = mouseMoveX;
//Atomic: recalibrate the accumulator. (redefine the movement accumulator for new "zero" point)
mouseMoveX -= result;

There is highly unlikely something happens between those instructions but if it is, i do not care, i do already got my "result" and redefine the movement accumulator for new "zero" point. If something changed between two atomic operations that something would be the only thing in the accumulator. This new "zero" point would be 99,9999...% zero. If not zero then very close to it. If it's not zero then this offset would not accumulate.

Unless of cource you calling mouseX() by separate threads, then you'll need to unite two atomic operations with mutex but that is highly unlikely you'll ever need to broke mouse input into separate threads with chaotic order.


P.S. Nice to see you are moving right direction with WM_INPUT 8). I do really hope WM_INPUT will let to read all the extended keys on keyboard.
_willow_
Hazed
Posts: 51
Joined: Mon Dec 24, 2007 2:03 am
Location: Russia
Contact:

Post by _willow_ »

You may find mouse wheel readings tricky too, that is how i did it in Berserker@Quake2:

Code: Select all

//The delta was set to 120 to allow Microsoft or other vendors to build finer-resolution wheels later,
//including perhaps a freely-rotating wheel with no notches. The expectation is that such a device
//would send more messages per rotation, but with a smaller value in each message. To support this
//possibility, you should either add the incoming delta values until WHEEL_DELTA is reached (so for a
//delta-rotation you get the same response), or scroll partial lines in response to the more frequent
//messages. You could also choose your scroll granularity and accumulate deltas until it is reached.
WHEEL_DELTA_collector += static_cast<short>(raw->data.mouse.usButtonData); //wheel delta, signed
int i = WHEEL_DELTA_collector / WHEEL_DELTA;
if (i < 0)
{
...
}
else if (i > 0)
{
...
}
WHEEL_DELTA_collector %= WHEEL_DELTA;
[url=http://quake2xp.quakedev.com]quake2xp[/url] audio engineer
byuu

Post by byuu »

Unless of cource you calling mouseX() by separate threads, then you'll need to unite two atomic operations with mutex but that is highly unlikely you'll ever need to broke mouse input into separate threads with chaotic order.
That's exactly what I'm doing :P
The RawInput thread will be its own, for the sole purpose of catching window messages safely (Qt / DirectInput may fight for them with GetMessage(0, ...) otherwise.)
P.S. Nice to see you are moving right direction with WM_INPUT Cool. I do really hope WM_INPUT will let to read all the extended keys on keyboard.
The reason I don't support other keys is mostly because I don't have a cross-platform way to name them something sane. And even when I tried anyway with something like keyNNN (n = random number), DirectInput was returning 0x80 for all my media keys.

So yeah, WM_INPUT might be better in that regard. I don't like the way you have to use MakeCode + Flags to distinguish left and right shift/control/alt, but ... it works. So whatever.
_willow_
Hazed
Posts: 51
Joined: Mon Dec 24, 2007 2:03 am
Location: Russia
Contact:

Post by _willow_ »

byuu wrote:
Unless of cource you calling mouseX() by separate threads, then you'll need to unite two atomic operations with mutex but that is highly unlikely you'll ever need to broke mouse input into separate threads with chaotic order.
That's exactly what I'm doing :P
The RawInput thread will be its own, for the sole purpose of catching window messages safely (Qt / DirectInput may fight for them with GetMessage(0, ...) otherwise.)
Sometimes i'm way too emotional. Really important is to read the mouse movement within a single thread otherwise it's just have no sense, you'll just broke your input. What are you going to do with those X&Y deltas? I do suggest to do the refresh function in a single place.

Code: Select all

DotheXthing_null(signed result){}

LRESULT wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { 
.. 
   if (input->data.mouse.lLastX)
   {
        //thread callbacks collection:
        DotheXthing_1(input->data.mouse.lLastX);
        DotheXthing_2(input->data.mouse.lLastX);
        DotheXthing_3(input->data.mouse.lLastX);
   }
..
}

//Atomic: to disable the callback:
DotheXthing_1 = DotheXthing_null;

//Atomic: to enable the callback:
DotheXthing_1 = DotheXthing_mycallback;
Like a pizza delivery, hot data right from the mouse drivers core for any possible realtime demands, any type of threads, concurrency, etc. Still no need for mutexes. Best of all you will win the ultimate latency here for each thread all together.

the idea is to make wndproc to be kind of router, when the actual thread reader would be:

Code: Select all

signed X_accumulator_for_this_thread

DotheXthing_mycallback(signed mouse_X)
{
        //Atomic: adjust the movement accumulator. 
        X_accumulator_for_this_thread += mouse_X; 
}

now you may do whatever you want in your local thread processing:

//Atomic: get the result.
Give_me_my_result_at_last = X_accumulator_for_this_thread;
//Atomic: redefine the thread-wide movement accumulator
X_accumulator_for_this_thread -= Give_me_my_result_at_last;

"Give_me_my_result_at_last" is at your disposal now..
Each thread would have their own movement accumulators and all of the threads would have the freedom to go totally offsinc or even hangup and do any duties they want like if they were connected directly to the mouse driver core.
[url=http://quake2xp.quakedev.com]quake2xp[/url] audio engineer
byuu

Post by byuu »

New WIP. This version adds RawInput support.

I strongly recommend just deleting the old config file, because all the old bindings won't work. You may also need to select the driver manually from the advanced tab (it should be the default if no config file is found.)

I now allow up to sixteen keyboards, sixteen mice and sixteen joypads to be uniquely identified and independently mappable. So if anyone wants to setup a 6-man-per tag-team game of N-warp Daisakusen, now you can. And $50 for the first person to take a picture of said event for me ;)

While ZSNES beat me to mouse support with ManyMouse, I win with "ManyKeyboard" :P

And if anyone wants to get me one of those SNES barcode battler games + hardware, I'll try and emulate a generic USB HID barcode scanner.

Let's see ... currently the mouse assignment from the UI won't work. You'll need to edit bsnes.cfg. The format is:

mouseNN.x, mouseNN.y, mouseNN.z, mouseNN.buttonXX
NN = 0 - 16, XX = 0 - 4

Need to plan how I want to design a mouse capture window, so that will be a while still.

Also didn't get around to the screensaver disable code just yet. Took me seven hours straight and I just barely finished the RawInput driver in time.

Testing would be greatly appreciated.
tetsuo55
Regular
Posts: 307
Joined: Sat Mar 04, 2006 3:17 pm

Post by tetsuo55 »

byuu wrote:I now allow up to sixteen keyboards, sixteen mice and sixteen joypads to be uniquely identified and independently mappable.
Awesome!

Some questions:
1. These (could) in theory all work at the same time? or is there a real limit.
2. What is the button and axis limit now with these changes? (per device)
byuu

Post by byuu »

Oh yeah, the dwSampleRate setting in the RID_DEVICE_INFO_MOUSE struct is useless. Blank on my optical / laser mice :(
tetsuo55 wrote:Awesome!

Some questions:
1. These (could) in theory all work at the same time? or is there a real limit.
2. What is the button and axis limit now with these changes? (per device)
1. Yes, they will all work at the same time.
2a. Keyboards [up to 16]: just the standard keys. Not even RawInput gives actual keycodes for extended media keys.
2b. Mice [up to 16]: up to three axes (Z, aka scroll wheel, is pretty useless though for the SNES), and up to 8 buttons (though the core APIs for both RawInput and Xlib only provide the first five button states.)
2c. Joypads [up to 16]: up to 8 POV hats (allows 4 directions per, so 32 unique states total), 16 axes (allows 2 directions per, so 32 unique states total), 96 buttons.

So in total? 16*115 + 16*(8+3) + 16*(32+32+96) = 1840 + 176 + 2560 = 4,576 unique, mappable buttons :P
Total bindable shortcuts for all emulated devices and UI items? 12*10 + 2*4 + 6 + 2*4 + 12 = 120+8+6+8+12 = 154.
Overkill = ~2,971%
Locked