Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a battle.net replacement - need to re-capture keyboard/mouse in windowed mode #371

Open
impromptu1583 opened this issue Dec 24, 2024 · 13 comments

Comments

@impromptu1583
Copy link

I recently built a peer to peer SNP network provider CrownLink for use with the broodwar project Cosmonarchy. I'm working on migrating it to a custom UI similar to battle.net. I'm building a custom UI with SFML.

I'm able to call ReleaseCapture() and ShowWindow(parent, SW_HIDE) when I load my interface, but when I want to switch back to the main UI with ShowWindow(parent, SW_SHOW) and SetCapture(parent) the mouse/keyboard focus is not correctly captured in windowed mode. It works fine in fullscreen mode.

The parent HWND in this scenario is passed to my code by starcraft and from my testing appears to be the actual 640x480 "fake" fullscreen window. Do you have any suggestions on making this code work properly when using CNC DDraw? Is there any API to recapture keyboard mouse so I could test if CNC DDraw is loaded then call a function to recapture keyboard/mouse?

Thank you

@FunkyFr3sh
Copy link
Owner

Are you sure you want to use SetCapture/ReleaseCapture? These functions are most of the time used incorrectly (you don't need them, they do more harm than good).

And why would you hide the parent? Can't you just create a child window with the same size as the parent?

Overall, you might only need to set "fixchild=0" so cnc-ddraw doesn't mess around with your child window and also set "nonexclusive=true" to make sure you are in fake fullscreen. Other than that there should be nothing you have to do

@impromptu1583
Copy link
Author

impromptu1583 commented Dec 24, 2024

I am fairly new to GUI programming and was following what blizzard did originally with battle.net for sc/bw. I'm not using directdraw for the ui I'm building (SFML is openGL) so it's not rendered within the cnc-ddraw window.

I was trying to get the parent window location/size but GetWindowRect always just yields (0, 0, 640, 480) for the window handle I have which is of course not useful. I suppose I could try to find the window handle of the cnc-ddraw window and read that.

As-is I can open my gui, call the appropriate code to create a lobby, then exit my gui but I'd like keyboard/mouse focus to automatically return to the main window once I do so. That's why I was trying to use SetCapture/ReleaseCapture.

here's a quick video (with audio) showing what's going on.

2024-12-24.14-25-00.remuxed.mp4

@FunkyFr3sh
Copy link
Owner

FunkyFr3sh commented Dec 24, 2024

cnc-ddraw does have a endless amount of hooks, all functions that return window size/position will return fake values. This is needed to trick fullscreen games to run in a window (it can't be disabled). But you can use DDGetProcAddress exported from cnc-ddraw to get the real functions.

Maybe that's the problem? I don't know if the GUI framework you are using can handle all these hooks.

I'm not sure if cnc-ddraw will be the best solution for what you're trying to do, adding another window that doesn't use DirectDraw would also add all kinds of limitations (upscaling wouldn't work anymore either)

Edit:
Just saw your video. So the problem is only that you need to click once on the window to lock the cursor? That shouldn't happen if the window was created by the same process. Is your child window spawned from a different exe?

@impromptu1583
Copy link
Author

impromptu1583 commented Dec 24, 2024

The child window is spawned from a library that's loaded (Blizzard .snp network provider files are just dlls), but it should actually still be the same process. Starcraft calls a storm function that calls a function in my library that loads the UI.

I know it's not the ideal way of doing things but that's actually how blizzard designed starcraft/brood war. The original battle.net implementation hides the main window and spawns a new window for battle.net then unhides the main window when you create a game. Not sure why they didn't just integrate it in the main UI... but it's not a proper child window.

Seems like I should be able to accomplish what I need with DDGetProcAddress though!

@FunkyFr3sh
Copy link
Owner

Hm, that's strange. Can you upload me a debug log so I can check what's going on? Your window also doesn't seem to be a child window from the looks of it. #44

The original battle.net does not hide the main window BTW (I've spent a lot time with that mess!). It creates a bunch of additional top level windows (not child windows) that are layered on top of the main window. Took quite some effort to get windowed mode working, I have to move each of these windows manually when the main window is being moved around

@impromptu1583
Copy link
Author

impromptu1583 commented Dec 24, 2024

Here you go:
cnc-ddraw-Starcraft-1.zip

Thank you for your assistance by the way! Yes. My window isn't a proper child window either.

Basically doing this:

ReleaseCapture();
ShowWindow(parent, SW_HIDE);   

auto window = sf::RenderWindow(sf::VideoMode({1280u, 960u}), "SFML Window");
shape.setFillColor(sf::Color(100, 250, 50));

// instantly closing to keep cnc-ddraw log file small
window.close();

ShowWindow(parent, SW_SHOW);
SetFocus(parent);
SetCapture(parent);

SFML has a method to create a drawing context within an existing window handle but I couldn't get it to work properly with the window handle I have.

@FunkyFr3sh
Copy link
Owner

It does look good in the logs actually.

You should only have to move the window on the exact same location as the main window and also call ShowCursor to make your cursor visible.

Only problem is that when you alt+tab it would unlock your cursor, then you have to click on the window again to lock the cursor. I could probably export a function for that.

But just to make it clear, if you click on the main window after your custom window was closed, is everything working fine then?

@impromptu1583
Copy link
Author

impromptu1583 commented Dec 24, 2024 via email

@FunkyFr3sh
Copy link
Owner

FunkyFr3sh commented Dec 24, 2024

Yes absolutely! This is merely a very small user friction point and not a big deal at all.

Ah nice! Well, I do have such code already for the original battle.net. cnc-ddraw detects when battle.net was closed and then does automatically lock the cursor. It would probably be the most simple way to do the same for your solution.

If you could give your window a unique name or class name then I could detect it and lock the cursor automatically

With the current name of your window it's not possible, the logic would trigger for other games as well
WindowName=
ClassName=STATIC

@impromptu1583
Copy link
Author

impromptu1583 commented Dec 24, 2024 via email

@FunkyFr3sh
Copy link
Owner

FunkyFr3sh commented Dec 24, 2024

That would be wonderful! How about I get a little bit farther down the development process and reach out once I have things more finalized? Thanks so much

Sure! Just let me know!

If there are issues, here's also a test build with CrownLink.snp added to the hook exception list. Not sure how well that will work, you'll have to try if it's better to do it with or without hooks.

ddraw.zip -

if (_strcmpi(mod_filename, "opengl32") == 0 ||

This should work without DDGetProcAddress

@veeq7
Copy link

veeq7 commented Dec 26, 2024

Hmm, not sure how much experience you have with SFML, but our best case scenario would be total ownership of the window and the ability to draw over it with SFML, it would allow us even more than just the battle.net replacements. Adding debug menus, console, overlay windows for missing UI, and etc trivial.

I am not sure what it takes to do it, but probably ditching cncdraw completely, but maybe there is a way to create an SFML drawing context inside of the existing cncdraw window, redrawing what the game already draws, and forwarding the events back to original handlers.

Not sure, just food for thought, something I wanted to do for a while, but haven't really investigated. Thanks for the help you have provided to @impromptu1583

EDIT: SFML or SDL

@FunkyFr3sh
Copy link
Owner

Some guys hooked "ImGui" into cnc-ddraw and added their own custom menus to the game, maybe that's an option too? At least you don't have to create another window for that. I have never used SFML, so I don't know if the same can be done with it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants