wget2 icon indicating copy to clipboard operation
wget2 copied to clipboard

fix(windows): restore console modes before exit

Open insomnimus opened this issue 1 year ago • 7 comments

On Windows, the console modes are altered by wget2 (via SetupConsoleHandlein libwget), to enable virtual terminal sequences. However it's not restored before exit; this causes problems such as key binds not working properly, even after the wget2 process exits (#311).

This PR addresses the problem by saving the console modes at the start of main, and then restoring them before main returns.

I'm not well versed in C let alone this code base, so the PR might not be up to the project's standards. Do let me know if I should change anything.

Fixes: #311

insomnimus avatar Apr 17 '24 23:04 insomnimus

What if a CTRL_C_EVENT or CTRL_BREAK_EVENT gets raised? AFAICS, there's no call to SetConsoleCtrlHandler() to trap and restore the terminal-state.

gvanem avatar Apr 18 '24 04:04 gvanem

Actually cancelling a download in that fashion still seems to trigger the code that restores console modes, maybe it's not necessary to call SetConsoleCtrlHandler?

I cannot determine why this works when ctrl-c/break gets issued. It still ends up restoring console modes.

insomnimus avatar Apr 18 '24 09:04 insomnimus

It still ends up restoring console modes.

Are you sure? Neither SetConsoleCtrlHandler() nor SetConsoleMode() are called in Wget2. And there's a difference in how conhost.exe handles Ctrl-C vs. Ctrl-Break: https://learn.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals

gvanem avatar Apr 19 '24 00:04 gvanem

Ah, you're right: ctrl-break still leaves the console modified; however ctrl-c does not.

So the solution is SetConsoleCtrlHandler, however since it takes a handler function, how should I go about not duplicating the cleanup code at the end of main? Also it looks like the previous console modes have to become globals now, which I'm not a fan of.

As a side note I'm still not sure why the patch works for ctrl-c.

insomnimus avatar Apr 19 '24 10:04 insomnimus

Also SetConsoleMode is actually called indirectly:

  • init in src/options.c
  • calls log_init in src/log.c
  • calls wget_console_init in libwget/console.c
  • calls SetupConsoleHandle also in libwget/console.c
  • which finally calls SetConsoleMode

That's how i diagnosed the issue at first: commenting out SetConsoleMode calls inside SetupConsoleHandle, which worked but is of course undesirable.

insomnimus avatar Apr 19 '24 10:04 insomnimus

Are there any suggestions on how to proceed?

insomnimus avatar Apr 29 '24 14:04 insomnimus

I think you should add your code to the global constructor and the global destructor.

The destructor should be called at program exit. Please test it and give a feedback.

rockdaboot avatar May 01 '24 17:05 rockdaboot

Sorry for the delay; I got hit with the seasonal flu.

I've moved the cleanup to libwget/init.c as you suggested:

  • The initial console modes are obtained and stored in INITIALIZER().
  • A handler is registered via SetConsoleCtrlHandler(), also in INITIALIZER().
  • The handler simply calls exit(2) if the signal is ctrl+c or ctrl+break; it ignores other signals, letting any other handler, if any, handle them. This triggers whatever's registered with atexit(), including our destructor global_exit().
  • The console modes are restored in global_exit().

Let me know if I should change anything.

insomnimus avatar May 12 '24 23:05 insomnimus

Oh also I've tested this and it works for both ctrl+c and ctrl+break.

insomnimus avatar May 12 '24 23:05 insomnimus

Thanks for your contribution, manually pushed (changed the commit message).

rockdaboot avatar May 19 '24 11:05 rockdaboot