Skip to content

The main window

NuclearMeltdown edited this page Oct 2, 2026 · 7 revisions

The main window

src/window_win32.cpp, src/tray_win32.cpp, src/ui/tray_menu.cpp, src/desktop_win32.cpp, src/app_ui.cpp (DrawContextMenu, DragBorderlessWindow), src/app.cpp (Tick), src/app_tray.cpp (BuildTrayMenu), src/ui/startup_notices.cpp (DrawWelcome), src/render/video_renderer.cpp (PictureSizeAt)

The preview window is an ordinary window by default: title bar, frame, sized by hand. Everything below is an option on top of that, and none of it touches the capture path — the frame age stays where it was with every one of them on.

Window size

The context menu has Window size with 1×, 2× and 3×, each showing the size it would set. The factor goes on the lines; the width follows the picture's aspect, with the same arithmetic as the Integer scaling mode. A 240-line SNES at 2× gets 480 lines of picture, not 480 lines of window.

  • The width is rounded up. The Integer fit floors, so a width rounded to nearest could come out one pixel short and the fit would drop to the next factor down.
  • A rotated picture takes the factor on its width instead, because that is where the source's lines now run.
  • The toolbar is added on top, so the picture gets the size, not the window.

Statistics opens a submenu with Compact, Normal and Full. Picking a level shows the statistics at that level; picking the level that is already on hides them. F1 shows and hides them at the level last chosen.

Toolbar in the same menu, the × on the bar and F3 all show and hide the toolbar; Settings → Display has the same switch.

  • A size that does not fit the work area is greyed out, measured with the frame the restored window would have. The window is moved back onto the screen if the new size would hang off its edge.
  • The current size is ticked. A maximised window has none.

The menu is disabled in fullscreen and before the first frame, when there is no picture to take a size from.

Shift keeps the shape

Resizing is free, as it always was. Holding Shift while dragging an edge keeps the picture's shape, the way graphics programs keep an image's proportions — the reverse of locking by default and unlocking with a key.

KeepAspect bends the rectangle Windows proposes in WM_SIZING:

  • A side edge sets the width; the height follows at the bottom.
  • The top or bottom edge sets the height; the width follows at the right.
  • A corner takes whichever of the two asks for more, and the opposite corner stays put.
  • The aspect is that of the picture, so the frame and the toolbar are taken off first and put back afterwards.
  • The minimum size is enforced here, on both sides together. Windows clamps afterwards one side at a time, which would bend the shape again.

Borderless

Borderless in the context menu, under Settings → Display or with Ctrl+Enter leaves only the picture. It is still a normal window: taskbar button, Alt+Tab, Snap, maximise and the minimise animation all stay.

That is the whole reason for how it is built. The window style stays WS_OVERLAPPEDWINDOW throughout, and the frame is taken away in WM_NCCALCSIZE by declaring the whole window to be inside. Changing the style to WS_POPUP instead would have been shorter, and would have cost the taskbar button and the rest with it.

  • Creation sends WM_NCCALCSIZE with wParam FALSE and a plain RECT, not the NCCALCSIZE_PARAMS it sends later. Both are handled, so a window started borderless has no frame from its first frame on.
  • Maximised, a window hangs its frame over the screen's edges. With the frame gone, the picture would reach past them by as much; it gets the work area instead, which also leaves the taskbar uncovered.
  • Resizing still works from the edges. WM_NCHITTEST gives back a strip as wide as the frame would have been (SM_CXSIZEFRAME plus SM_CXPADDEDBORDER at the window's DPI).
  • WM_NCACTIVATE is passed on with lParam −1. Left alone, Windows paints the title bar it believes is there over the picture whenever the focus changes.
  • Windows 11 gets square corners and no outline through DWM. A rounded corner would cut into the picture and the outline would lie on it. Earlier versions refuse the attributes and keep their look.
  • Switching keeps the picture where it is on screen; the frame goes around it or away from it. The alternative, the window's corner staying put, makes the picture jump by the width of the frame. A picture at the top of the screen is moved down so the new title bar does not end up above it.
  • Fullscreen has no frame either way. Switching while in fullscreen adjusts the stored window it goes back to, so that window keeps the same inside.

Moving it by the picture

Without a title bar the window is moved by dragging the picture.

  • Only a drag past Dear ImGui's threshold becomes a move. A plain click stays a click and still counts towards a double click.
  • The press must start on the bare picture — not on the compare divider, not in a menu or panel.
  • The move itself is what a press on a title bar does: WM_NCLBUTTONDOWN with HTCAPTION, so Snap and the monitor-to-monitor behaviour are Windows' own. Dear ImGui is told about the release afterwards, because the move loop keeps it.

Two bugs came out of testing it:

  • The window lagged behind the pointer. A drag of 150 × 100 pixels moved it by 142 × 95. The move loop starts from where the pointer was when the message was posted, and it had already travelled while the drag was being recognised. The window now catches up to GetMessagePos before the loop starts, so the point that was grabbed stays under the pointer.
  • A double click out of fullscreen put the window at 0,0. The second click lands on the fullscreen picture; leaving fullscreen moves the window away under the pointer, and that jump read as a drag. The second click of a double click no longer arms a move.

The red dot while recording

While a recording runs, the taskbar button carries a small red dot, the red of the record button. Screen readers get Recording as its description.

  • It is an overlay icon, ITaskbarList3::SetOverlayIcon, drawn at run time at the small-icon size.
  • Windows 11 lays the overlay square over the icon's top-right corner, roughly centred on it. A dot filling the square covered most of the icon, so the dot is drawn small in the square's top-right corner and sits beside the icon instead. A dark, half-transparent ring keeps the two apart where they touch.
  • Explorer forgets overlays when it restarts. TaskbarButtonCreated is sent again then, and the dot is put back. (Untested: Explorer was not restarted for it.)
  • The description is cleared with an empty string, not with none. Passing a null description when removing the overlay left Recording behind for screen readers.
  • It is driven from Tick, compared against the recorder's state every frame, so it also goes when a recording ends by itself, such as before the drive fills up.

The tray icon

An icon in the notification area holds quick actions, for what the context menu has grown too full to reach quickly. Left click brings the window forward, right click opens the menu.

By default the menu has recording, both screenshots (file and clipboard), both output folders and the settings. Under Settings → General → Notification area more can be ticked: fullscreen, always on top, borderless, mute, freeze, the virtual camera, the profiles and restarting the capture. The icon itself can be switched off there, or from its own menu (Icon in the notification area).

  • Nothing hides into the tray. The window keeps its taskbar button whether the icon is on or not.
  • Windows 11 puts new icons into the overflow behind the arrow. Dragging it onto the taskbar, or switching it on in the taskbar's settings, keeps it in sight.
  • The menu is drawn by qBlank, with the same look as every other menu in it, and follows the app's theme. It is a small window of its own (src/ui/tray_menu.cpp) with its own Direct3D device and ImGui context, like the settings window, made the first time it opens. It stands above the taskbar, or below it when the taskbar is at the top, and closes on a pick, a click elsewhere or Esc.
  • The profiles unfold inside the menu, below their row, rather than flying out to the side: nothing can reach out of the menu's window. The menu grows away from the taskbar, so another row slides under the pointer; a click there counts only once the pointer has moved, so a double click cannot pick a profile by accident.
  • Nothing blocks the picture. The native tray menu was a modal loop and had to live on a thread of its own. The icon still does, but only to report a click; the menu is drawn in the main loop like the settings.
  • While recording the tooltip says so, and the record entry stops it.
  • Explorer forgets tray icons when it restarts. TaskbarCreated is sent then, and the icon is put back. (Untested, as with the red dot.)
  • The virtual camera entry opens the settings instead while the camera is not installed, since installing it needs a UAC prompt.
  • src/tray.h holds the icon, src/ui/tray_menu.h the menu as plain data; only src/tray_win32.cpp and the window layer know Windows.

Start menu and desktop

Settings → General → Shortcuts has two buttons: Add to the Start menu and Add to the desktop, each turning into Remove… once the shortcut is there. On the very first start — no settings file yet — a small dialog offers the ones that are missing, both ticked; No thanks closes it for good.

  • Buttons, not checkboxes. They act at once. A checkbox would wait for Apply, and Discard could not take a file back off the desktop.
  • A shortcut counts only if it starts this copy. One pointing at another qBlank shows as missing; adding points it here, removing leaves it alone.
  • Moved the program? Add them again; that points them at the new place.
  • For the current user only, so no UAC prompt. src/desktop.h has HasShortcut, CreateShortcut and RemoveShortcut; src/desktop_win32.cpp writes the .lnk.

Clone this wiki locally