mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Merge demo screenshot features from 'pre-0.77'.
This commit is contained in:
commit
dec7d7fce7
@ -14,6 +14,7 @@
|
||||
#cmakedefine01 HAVE_GETNAMEDPIPECLIENTPROCESSID
|
||||
#cmakedefine01 HAVE_SETDEFAULTDLLDIRECTORIES
|
||||
#cmakedefine01 HAVE_STRTOUMAX
|
||||
#cmakedefine01 HAVE_DWMAPI_H
|
||||
|
||||
#cmakedefine NOT_X_WINDOWS
|
||||
|
||||
|
@ -51,6 +51,8 @@ check_symbol_exists(GetNamedPipeClientProcessId "windows.h"
|
||||
HAVE_GETNAMEDPIPECLIENTPROCESSID)
|
||||
check_symbol_exists(CreatePseudoConsole "windows.h" HAVE_CONPTY)
|
||||
|
||||
check_include_files("windows.h;dwmapi.h" HAVE_DWMAPI_H)
|
||||
|
||||
check_c_source_compiles("
|
||||
#include <windows.h>
|
||||
GCP_RESULTSW gcpw;
|
||||
|
@ -32,11 +32,8 @@
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
static char *null_init(const BackendVtable *, Seat *, Backend **, LogContext *,
|
||||
Conf *, const char *, int, char **, bool, bool);
|
||||
static char *loop_init(const BackendVtable *, Seat *, Backend **, LogContext *,
|
||||
Conf *, const char *, int, char **, bool, bool);
|
||||
static void null_free(Backend *);
|
||||
static void loop_free(Backend *);
|
||||
static void null_reconfig(Backend *, Conf *);
|
||||
static void null_send(Backend *, const char *, size_t);
|
||||
@ -55,8 +52,8 @@ static void null_unthrottle(Backend *, size_t);
|
||||
static int null_cfg_info(Backend *);
|
||||
|
||||
const BackendVtable null_backend = {
|
||||
.init = null_init,
|
||||
.free = null_free,
|
||||
.init = loop_init,
|
||||
.free = loop_free,
|
||||
.reconfig = null_reconfig,
|
||||
.send = null_send,
|
||||
.sendbuffer = null_sendbuffer,
|
||||
@ -106,17 +103,6 @@ struct loop_state {
|
||||
size_t sendbuffer;
|
||||
};
|
||||
|
||||
static char *null_init(const BackendVtable *vt, Seat *seat,
|
||||
Backend **backend_handle, LogContext *logctx,
|
||||
Conf *conf, const char *host, int port,
|
||||
char **realhost, bool nodelay, bool keepalive) {
|
||||
/* No local authentication phase in this protocol */
|
||||
seat_set_trust_status(seat, false);
|
||||
|
||||
*backend_handle = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *loop_init(const BackendVtable *vt, Seat *seat,
|
||||
Backend **backend_handle, LogContext *logctx,
|
||||
Conf *conf, const char *host, int port,
|
||||
@ -127,15 +113,14 @@ static char *loop_init(const BackendVtable *vt, Seat *seat,
|
||||
seat_set_trust_status(seat, false);
|
||||
|
||||
st->seat = seat;
|
||||
st->backend.vt = vt;
|
||||
*backend_handle = &st->backend;
|
||||
|
||||
*realhost = dupstr(host);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void null_free(Backend *be)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void loop_free(Backend *be)
|
||||
{
|
||||
struct loop_state *st = container_of(be, struct loop_state, backend);
|
||||
|
18
settings.c
18
settings.c
@ -1307,6 +1307,8 @@ static int sessioncmp(const void *av, const void *bv)
|
||||
return strcmp(a, b); /* otherwise, compare normally */
|
||||
}
|
||||
|
||||
bool sesslist_demo_mode = false;
|
||||
|
||||
void get_sesslist(struct sesslist *list, bool allocate)
|
||||
{
|
||||
int i;
|
||||
@ -1316,12 +1318,18 @@ void get_sesslist(struct sesslist *list, bool allocate)
|
||||
if (allocate) {
|
||||
strbuf *sb = strbuf_new();
|
||||
|
||||
if ((handle = enum_settings_start()) != NULL) {
|
||||
while (enum_settings_next(handle, sb))
|
||||
put_byte(sb, '\0');
|
||||
enum_settings_finish(handle);
|
||||
if (sesslist_demo_mode) {
|
||||
put_asciz(sb, "demo-server");
|
||||
put_asciz(sb, "demo-server-2");
|
||||
} else {
|
||||
if ((handle = enum_settings_start()) != NULL) {
|
||||
while (enum_settings_next(handle, sb))
|
||||
put_byte(sb, '\0');
|
||||
enum_settings_finish(handle);
|
||||
}
|
||||
put_byte(sb, '\0');
|
||||
}
|
||||
put_byte(sb, '\0');
|
||||
|
||||
list->buffer = strbuf_to_str(sb);
|
||||
|
||||
/*
|
||||
|
@ -28,6 +28,7 @@ add_sources_from_current_dir(utils
|
||||
utils/platform_get_x_display.c
|
||||
utils/registry_get_string.c
|
||||
utils/request_file.c
|
||||
utils/screenshot.c
|
||||
utils/security.c
|
||||
utils/split_into_argv.c
|
||||
utils/version.c
|
||||
|
@ -388,6 +388,8 @@ static void create_controls(HWND hwnd, char *path)
|
||||
}
|
||||
}
|
||||
|
||||
const char *dialog_box_demo_screenshot_filename = NULL;
|
||||
|
||||
/*
|
||||
* This function is the configuration box.
|
||||
* (Being a dialog procedure, in general it returns 0 if the default
|
||||
@ -396,6 +398,7 @@ static void create_controls(HWND hwnd, char *path)
|
||||
static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
const int DEMO_SCREENSHOT_TIMER_ID = 1230;
|
||||
HWND hw, treeview;
|
||||
struct treeview_faff tvfaff;
|
||||
int ret;
|
||||
@ -565,6 +568,21 @@ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
|
||||
* spurious firing during the above setup procedure.
|
||||
*/
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, 1);
|
||||
|
||||
if (dialog_box_demo_screenshot_filename)
|
||||
SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
|
||||
return 0;
|
||||
case WM_TIMER:
|
||||
if (dialog_box_demo_screenshot_filename &&
|
||||
(UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
|
||||
KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
|
||||
const char *err = save_screenshot(
|
||||
hwnd, dialog_box_demo_screenshot_filename);
|
||||
if (err)
|
||||
MessageBox(hwnd, err, "Demo screenshot failure",
|
||||
MB_OK | MB_ICONERROR);
|
||||
SaneEndDialog(hwnd, 0);
|
||||
}
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
/*
|
||||
|
@ -759,4 +759,7 @@ bool aux_match_arg(AuxMatchOpt *amo, char **val);
|
||||
bool aux_match_opt(AuxMatchOpt *amo, char **val, const char *optname, ...);
|
||||
bool aux_match_done(AuxMatchOpt *amo);
|
||||
|
||||
char *save_screenshot(HWND hwnd, const char *outfile);
|
||||
void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend);
|
||||
|
||||
#endif /* PUTTY_WINDOWS_PLATFORM_H */
|
||||
|
@ -45,3 +45,7 @@ const wchar_t *get_app_user_model_id(void)
|
||||
{
|
||||
return L"SimonTatham.Pterm";
|
||||
}
|
||||
|
||||
void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend)
|
||||
{
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
#include "putty.h"
|
||||
#include "storage.h"
|
||||
|
||||
extern bool sesslist_demo_mode;
|
||||
extern const char *dialog_box_demo_screenshot_filename;
|
||||
static strbuf *demo_terminal_data = NULL;
|
||||
static const char *terminal_demo_screenshot_filename;
|
||||
|
||||
void gui_term_process_cmdline(Conf *conf, char *cmdline)
|
||||
{
|
||||
char *p;
|
||||
bool special_launchable_argument = false;
|
||||
bool demo_config_box = false;
|
||||
|
||||
settings_set_default_protocol(be_default_protocol);
|
||||
/* Find the appropriate default port. */
|
||||
@ -81,6 +87,29 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
|
||||
} else if (!strcmp(p, "-pgpfp")) {
|
||||
pgp_fingerprints_msgbox(NULL);
|
||||
exit(1);
|
||||
} else if (!strcmp(p, "-demo-config-box")) {
|
||||
if (i+1 >= argc) {
|
||||
cmdline_error("%s expects an output filename", p);
|
||||
} else {
|
||||
demo_config_box = true;
|
||||
dialog_box_demo_screenshot_filename = argv[++i];
|
||||
}
|
||||
} else if (!strcmp(p, "-demo-terminal")) {
|
||||
if (i+2 >= argc) {
|
||||
cmdline_error("%s expects input and output filenames", p);
|
||||
} else {
|
||||
const char *infile = argv[++i];
|
||||
terminal_demo_screenshot_filename = argv[++i];
|
||||
FILE *fp = fopen(infile, "rb");
|
||||
if (!fp)
|
||||
cmdline_error("can't open input file '%s'", infile);
|
||||
demo_terminal_data = strbuf_new();
|
||||
char buf[4096];
|
||||
int retd;
|
||||
while ((retd = fread(buf, 1, sizeof(buf), fp)) > 0)
|
||||
put_data(demo_terminal_data, buf, retd);
|
||||
fclose(fp);
|
||||
}
|
||||
} else if (*p != '-') {
|
||||
cmdline_error("unexpected argument \"%s\"", p);
|
||||
} else {
|
||||
@ -91,13 +120,26 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
|
||||
|
||||
cmdline_run_saved(conf);
|
||||
|
||||
/*
|
||||
* Bring up the config dialog if the command line hasn't
|
||||
* (explicitly) specified a launchable configuration.
|
||||
*/
|
||||
if (!(special_launchable_argument || cmdline_host_ok(conf))) {
|
||||
if (!do_config(conf))
|
||||
cleanup_exit(0);
|
||||
if (demo_config_box) {
|
||||
sesslist_demo_mode = true;
|
||||
load_open_settings(NULL, conf);
|
||||
conf_set_str(conf, CONF_host, "demo-server.example.com");
|
||||
do_config(conf);
|
||||
cleanup_exit(0);
|
||||
} else if (demo_terminal_data) {
|
||||
/* Ensure conf will cause an immediate session launch */
|
||||
load_open_settings(NULL, conf);
|
||||
conf_set_str(conf, CONF_host, "demo-server.example.com");
|
||||
conf_set_int(conf, CONF_close_on_exit, FORCE_OFF);
|
||||
} else {
|
||||
/*
|
||||
* Bring up the config dialog if the command line hasn't
|
||||
* (explicitly) specified a launchable configuration.
|
||||
*/
|
||||
if (!(special_launchable_argument || cmdline_host_ok(conf))) {
|
||||
if (!do_config(conf))
|
||||
cleanup_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
prepare_session(conf);
|
||||
@ -105,6 +147,10 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline)
|
||||
|
||||
const struct BackendVtable *backend_vt_from_conf(Conf *conf)
|
||||
{
|
||||
if (demo_terminal_data) {
|
||||
return &null_backend;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select protocol. This is farmed out into a table in a
|
||||
* separate file to enable an ssh-free variant.
|
||||
@ -125,3 +171,19 @@ const wchar_t *get_app_user_model_id(void)
|
||||
{
|
||||
return L"SimonTatham.PuTTY";
|
||||
}
|
||||
|
||||
static void demo_terminal_screenshot(void *ctx, unsigned long now)
|
||||
{
|
||||
HWND hwnd = (HWND)ctx;
|
||||
save_screenshot(hwnd, terminal_demo_screenshot_filename);
|
||||
cleanup_exit(0);
|
||||
}
|
||||
|
||||
void gui_terminal_ready(HWND hwnd, Seat *seat, Backend *backend)
|
||||
{
|
||||
if (demo_terminal_data) {
|
||||
ptrlen data = ptrlen_from_strbuf(demo_terminal_data);
|
||||
seat_stdout(seat, data.ptr, data.len);
|
||||
schedule_timer(TICKSPERSEC, demo_terminal_screenshot, (void *)hwnd);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
#define DEFAULT_EDCURVE_INDEX 0
|
||||
|
||||
static char *cmdline_keyfile = NULL;
|
||||
static ptrlen cmdline_demo_keystr;
|
||||
static const char *demo_screenshot_filename = NULL;
|
||||
|
||||
/*
|
||||
* Print a modal (Really Bad) message box and perform a fatal exit.
|
||||
@ -992,6 +994,65 @@ void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option)
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ui_after_load(HWND hwnd, struct MainDlgState *state,
|
||||
const char *passphrase, int type,
|
||||
RSAKey *newkey1, ssh2_userkey *newkey2)
|
||||
{
|
||||
SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, passphrase);
|
||||
SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, passphrase);
|
||||
|
||||
if (type == SSH_KEYTYPE_SSH1) {
|
||||
char *fingerprint, *savecomment;
|
||||
|
||||
state->ssh2 = false;
|
||||
state->commentptr = &state->key.comment;
|
||||
state->key = *newkey1; /* structure copy */
|
||||
|
||||
/*
|
||||
* Set the key fingerprint.
|
||||
*/
|
||||
savecomment = state->key.comment;
|
||||
state->key.comment = NULL;
|
||||
fingerprint = rsa_ssh1_fingerprint(&state->key);
|
||||
state->key.comment = savecomment;
|
||||
SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint);
|
||||
sfree(fingerprint);
|
||||
|
||||
/*
|
||||
* Construct a decimal representation of the key, for pasting
|
||||
* into .ssh/authorized_keys on a Unix box.
|
||||
*/
|
||||
setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key);
|
||||
} else {
|
||||
char *fp;
|
||||
char *savecomment;
|
||||
|
||||
state->ssh2 = true;
|
||||
state->commentptr = &state->ssh2key.comment;
|
||||
state->ssh2key = *newkey2; /* structure copy */
|
||||
sfree(newkey2);
|
||||
|
||||
savecomment = state->ssh2key.comment;
|
||||
state->ssh2key.comment = NULL;
|
||||
fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
|
||||
state->ssh2key.comment = savecomment;
|
||||
|
||||
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
|
||||
sfree(fp);
|
||||
|
||||
setupbigedit2(hwnd, IDC_KEYDISPLAY,
|
||||
IDC_PKSTATIC, &state->ssh2key);
|
||||
}
|
||||
SetDlgItemText(hwnd, IDC_COMMENTEDIT,
|
||||
*state->commentptr);
|
||||
|
||||
/*
|
||||
* Finally, hide the progress bar and show the key data.
|
||||
*/
|
||||
ui_set_state(hwnd, state, 2);
|
||||
state->key_exists = true;
|
||||
}
|
||||
|
||||
void load_key_file(HWND hwnd, struct MainDlgState *state,
|
||||
Filename *filename, bool was_import_cmd)
|
||||
{
|
||||
@ -1081,65 +1142,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state,
|
||||
* Now update the key controls with all the
|
||||
* key data.
|
||||
*/
|
||||
{
|
||||
SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
|
||||
passphrase);
|
||||
SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
|
||||
passphrase);
|
||||
if (type == SSH_KEYTYPE_SSH1) {
|
||||
char *fingerprint, *savecomment;
|
||||
|
||||
state->ssh2 = false;
|
||||
state->commentptr = &state->key.comment;
|
||||
state->key = newkey1;
|
||||
|
||||
/*
|
||||
* Set the key fingerprint.
|
||||
*/
|
||||
savecomment = state->key.comment;
|
||||
state->key.comment = NULL;
|
||||
fingerprint = rsa_ssh1_fingerprint(&state->key);
|
||||
state->key.comment = savecomment;
|
||||
SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint);
|
||||
sfree(fingerprint);
|
||||
|
||||
/*
|
||||
* Construct a decimal representation
|
||||
* of the key, for pasting into
|
||||
* .ssh/authorized_keys on a Unix box.
|
||||
*/
|
||||
setupbigedit1(hwnd, IDC_KEYDISPLAY,
|
||||
IDC_PKSTATIC, &state->key);
|
||||
} else {
|
||||
char *fp;
|
||||
char *savecomment;
|
||||
|
||||
state->ssh2 = true;
|
||||
state->commentptr =
|
||||
&state->ssh2key.comment;
|
||||
state->ssh2key = *newkey2; /* structure copy */
|
||||
sfree(newkey2);
|
||||
|
||||
savecomment = state->ssh2key.comment;
|
||||
state->ssh2key.comment = NULL;
|
||||
fp = ssh2_fingerprint(state->ssh2key.key, state->fptype);
|
||||
state->ssh2key.comment = savecomment;
|
||||
|
||||
SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
|
||||
sfree(fp);
|
||||
|
||||
setupbigedit2(hwnd, IDC_KEYDISPLAY,
|
||||
IDC_PKSTATIC, &state->ssh2key);
|
||||
}
|
||||
SetDlgItemText(hwnd, IDC_COMMENTEDIT,
|
||||
*state->commentptr);
|
||||
}
|
||||
/*
|
||||
* Finally, hide the progress bar and show
|
||||
* the key data.
|
||||
*/
|
||||
ui_set_state(hwnd, state, 2);
|
||||
state->key_exists = true;
|
||||
update_ui_after_load(hwnd, state, passphrase, type, &newkey1, newkey2);
|
||||
|
||||
/*
|
||||
* If the user has imported a foreign key
|
||||
@ -1205,6 +1208,7 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state)
|
||||
static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
const int DEMO_SCREENSHOT_TIMER_ID = 1230;
|
||||
static const char entropy_msg[] =
|
||||
"Please generate some randomness by moving the mouse over the blank area.";
|
||||
struct MainDlgState *state;
|
||||
@ -1428,9 +1432,30 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
|
||||
Filename *fn = filename_from_str(cmdline_keyfile);
|
||||
load_key_file(hwnd, state, fn, false);
|
||||
filename_free(fn);
|
||||
} else if (cmdline_demo_keystr.ptr) {
|
||||
BinarySource src[1];
|
||||
BinarySource_BARE_INIT_PL(src, cmdline_demo_keystr);
|
||||
const char *errmsg;
|
||||
ssh2_userkey *k = ppk_load_s(src, NULL, &errmsg);
|
||||
assert(!errmsg);
|
||||
|
||||
update_ui_after_load(hwnd, state, "demo passphrase",
|
||||
SSH_KEYTYPE_SSH2, NULL, k);
|
||||
|
||||
SetTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID, TICKSPERSEC, NULL);
|
||||
}
|
||||
|
||||
return 1;
|
||||
case WM_TIMER:
|
||||
if ((UINT_PTR)wParam == DEMO_SCREENSHOT_TIMER_ID) {
|
||||
KillTimer(hwnd, DEMO_SCREENSHOT_TIMER_ID);
|
||||
const char *err = save_screenshot(hwnd, demo_screenshot_filename);
|
||||
if (err)
|
||||
MessageBox(hwnd, err, "Demo screenshot failure",
|
||||
MB_OK | MB_ICONERROR);
|
||||
EndDialog(hwnd, 0);
|
||||
}
|
||||
return 0;
|
||||
case WM_MOUSEMOVE:
|
||||
state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
if (state->entropy && state->entropy_got < state->entropy_required) {
|
||||
@ -2175,6 +2200,22 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
opt_error("unrecognised PPK parameter '%s'\n", val);
|
||||
}
|
||||
}
|
||||
} else if (match_optval("-demo-screenshot")) {
|
||||
demo_screenshot_filename = val;
|
||||
cmdline_demo_keystr = PTRLEN_LITERAL(
|
||||
"PuTTY-User-Key-File-3: ssh-ed25519\n"
|
||||
"Encryption: none\n"
|
||||
"Comment: ed25519-key-20220402\n"
|
||||
"Public-Lines: 2\n"
|
||||
"AAAAC3NzaC1lZDI1NTE5AAAAILzuIFwZ"
|
||||
"8ZhgOlilcSb+9zPuCf/DmKJiloVlmWGy\n"
|
||||
"xa/F\n"
|
||||
"Private-Lines: 1\n"
|
||||
"AAAAIPca6vLwtB2NJhZUpABQISR0gcQH8jjQLta19VyzA3wc\n"
|
||||
"Private-MAC: 1159e9628259b35933b397379bbe8a14"
|
||||
"a1f1d97fe91e446e45a9581a3408b70e\n");
|
||||
params->keybutton = IDC_KEYSSH2EDDSA;
|
||||
argbits = 255;
|
||||
} else {
|
||||
opt_error("unrecognised option '%s'\n", amo.argv[amo.index]);
|
||||
}
|
||||
|
126
windows/utils/screenshot.c
Normal file
126
windows/utils/screenshot.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include "putty.h"
|
||||
|
||||
#if HAVE_DWMAPI_H
|
||||
|
||||
#include <dwmapi.h>
|
||||
|
||||
char *save_screenshot(HWND hwnd, const char *outfile)
|
||||
{
|
||||
HDC dcWindow = NULL, dcSave = NULL;
|
||||
HBITMAP bmSave = NULL;
|
||||
uint8_t *buffer = NULL;
|
||||
char *err = NULL;
|
||||
|
||||
static HMODULE dwmapi_module;
|
||||
DECL_WINDOWS_FUNCTION(static, HRESULT, DwmGetWindowAttribute,
|
||||
(HWND, DWORD, PVOID, DWORD));
|
||||
|
||||
if (!dwmapi_module) {
|
||||
dwmapi_module = load_system32_dll("dwmapi.dll");
|
||||
GET_WINDOWS_FUNCTION(dwmapi_module, DwmGetWindowAttribute);
|
||||
}
|
||||
|
||||
dcWindow = GetDC(NULL);
|
||||
if (!dcWindow) {
|
||||
err = dupprintf("GetDC(window): %s", win_strerror(GetLastError()));
|
||||
goto out;
|
||||
}
|
||||
|
||||
int x, y, w, h;
|
||||
RECT wr;
|
||||
|
||||
/* Use DwmGetWindowAttribute in place of GetWindowRect to exclude
|
||||
* drop shadow, otherwise we get a load of unwanted desktop
|
||||
* background under the shadow */
|
||||
if (p_DwmGetWindowAttribute &&
|
||||
0 <= p_DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&wr, sizeof(wr))) {
|
||||
x = wr.left;
|
||||
y = wr.top;
|
||||
w = wr.right - wr.left;
|
||||
h = wr.bottom - wr.top;
|
||||
} else {
|
||||
BITMAP bmhdr;
|
||||
memset(&bmhdr, 0, sizeof(bmhdr));
|
||||
GetObject(GetCurrentObject(dcWindow, OBJ_BITMAP),
|
||||
sizeof(bmhdr), &bmhdr);
|
||||
x = y = 0;
|
||||
w = bmhdr.bmWidth;
|
||||
h = bmhdr.bmHeight;
|
||||
}
|
||||
|
||||
dcSave = CreateCompatibleDC(dcWindow);
|
||||
if (!dcSave) {
|
||||
err = dupprintf("CreateCompatibleDC(desktop window dc): %s",
|
||||
win_strerror(GetLastError()));
|
||||
goto out;
|
||||
}
|
||||
|
||||
bmSave = CreateCompatibleBitmap(dcWindow, w, h);
|
||||
if (!bmSave) {
|
||||
err = dupprintf("CreateCompatibleBitmap: %s",
|
||||
win_strerror(GetLastError()));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!SelectObject(dcSave, bmSave)) {
|
||||
err = dupprintf("SelectObject: %s", win_strerror(GetLastError()));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!BitBlt(dcSave, 0, 0, w, h, dcWindow, x, y, SRCCOPY)) {
|
||||
err = dupprintf("BitBlt: %s", win_strerror(GetLastError()));
|
||||
goto out;
|
||||
}
|
||||
|
||||
BITMAPINFO bmInfo;
|
||||
memset(&bmInfo, 0, sizeof(bmInfo));
|
||||
bmInfo.bmiHeader.biSize = sizeof(bmInfo.bmiHeader);
|
||||
bmInfo.bmiHeader.biWidth = w;
|
||||
bmInfo.bmiHeader.biHeight = h;
|
||||
bmInfo.bmiHeader.biPlanes = 1;
|
||||
bmInfo.bmiHeader.biBitCount = 32;
|
||||
bmInfo.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
size_t bmPixels = (size_t)w*h, bmBytes = bmPixels * 4;
|
||||
buffer = snewn(bmBytes, uint8_t);
|
||||
|
||||
if (!GetDIBits(dcWindow, bmSave, 0, h, buffer, &bmInfo, DIB_RGB_COLORS))
|
||||
err = dupprintf("GetDIBits (get data): %s",
|
||||
win_strerror(GetLastError()));
|
||||
|
||||
FILE *fp = fopen(outfile, "wb");
|
||||
if (!fp) {
|
||||
err = dupprintf("'%s': unable to open file", outfile);
|
||||
goto out;
|
||||
}
|
||||
|
||||
BITMAPFILEHEADER bmFileHdr;
|
||||
bmFileHdr.bfType = 'B' | ('M' << 8);
|
||||
bmFileHdr.bfSize = sizeof(bmFileHdr) + sizeof(bmInfo.bmiHeader) + bmBytes;
|
||||
bmFileHdr.bfOffBits = sizeof(bmFileHdr) + sizeof(bmInfo.bmiHeader);
|
||||
fwrite((void *)&bmFileHdr, 1, sizeof(bmFileHdr), fp);
|
||||
fwrite((void *)&bmInfo.bmiHeader, 1, sizeof(bmInfo.bmiHeader), fp);
|
||||
fwrite((void *)buffer, 1, bmBytes, fp);
|
||||
fclose(fp);
|
||||
|
||||
out:
|
||||
if (dcWindow)
|
||||
ReleaseDC(NULL, dcWindow);
|
||||
if (bmSave)
|
||||
DeleteObject(bmSave);
|
||||
if (dcSave)
|
||||
DeleteObject(dcSave);
|
||||
sfree(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
#else /* HAVE_DWMAPI_H */
|
||||
|
||||
/* Without <dwmapi.h> we can't get the right window rectangle */
|
||||
char *save_screenshot(HWND hwnd, const char *outfile)
|
||||
{
|
||||
return dupstr("Demo screenshots not compiled in to this build");
|
||||
}
|
||||
|
||||
#endif /* HAVE_DWMAPI_H */
|
@ -810,6 +810,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
|
||||
term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd);
|
||||
UpdateWindow(wgs.term_hwnd);
|
||||
|
||||
gui_terminal_ready(wgs.term_hwnd, &wgs.seat, backend);
|
||||
|
||||
while (1) {
|
||||
int n;
|
||||
DWORD timeout;
|
||||
|
Loading…
Reference in New Issue
Block a user