The latest scanner settings are now automatically saved in the HKEY_CURRENT_USER\Software\ScannersSettings<Vendor><Product> path. This modification was tested and worked correctly.
From: Ivan Lyugaev valy@etersoft.ru
The latest scanner settings are now automatically saved in the HKEY_CURRENT_USER\Software\ScannersSettings<Vendor><Product> path --- dlls/sane.ds/Makefile.in | 5 +- dlls/sane.ds/cfg.c | 148 +++++++++++++++++++++++ dlls/sane.ds/cfg.h | 48 ++++++++ dlls/sane.ds/sane_main.c | 3 + dlls/sane.ds/ui.c | 254 ++++++++++++++++++++++++++++++++++++--- 5 files changed, 440 insertions(+), 18 deletions(-) create mode 100644 dlls/sane.ds/cfg.c create mode 100644 dlls/sane.ds/cfg.h
diff --git a/dlls/sane.ds/Makefile.in b/dlls/sane.ds/Makefile.in index eed1acdf918..28549e75740 100644 --- a/dlls/sane.ds/Makefile.in +++ b/dlls/sane.ds/Makefile.in @@ -1,6 +1,6 @@ MODULE = sane.ds UNIXLIB = sane.so -IMPORTS = comctl32 user32 gdi32 +IMPORTS = comctl32 user32 gdi32 kernelbase UNIX_LIBS = $(SANE_LIBS) UNIX_CFLAGS = $(SANE_CFLAGS)
@@ -12,4 +12,5 @@ SOURCES = \ sane.rc \ sane_main.c \ ui.c \ - unixlib.c + unixlib.c \ + cfg.c diff --git a/dlls/sane.ds/cfg.c b/dlls/sane.ds/cfg.c new file mode 100644 index 00000000000..cbbdf834298 --- /dev/null +++ b/dlls/sane.ds/cfg.c @@ -0,0 +1,148 @@ +/* +* TWAIN32 Configuration Manager +* +* Copyright 2025 Ivan Lyugaev +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include "wine/debug.h" + +#include "cfg.h" + +WINE_DEFAULT_DEBUG_CHANNEL( twain ); + +LSTATUS get_info_key( WCHAR* path, HKEY* h_key, DWORD* dispos ) +{ + WCHAR reg_path[MAX_PATH]; + swprintf( reg_path, MAX_PATH, L"Software\ScannersSettings\%s", path ); + + return RegCreateKeyExW( + HKEY_CURRENT_USER, + reg_path, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + h_key, + dispos + ); +} + +BOOL is_exist_reg( WCHAR* path ) +{ + HKEY h_key; + DWORD dispos; + LSTATUS res; + + res = get_info_key(path, &h_key, &dispos); + + if ( res != ERROR_SUCCESS ) + { + ERR( "RegCreateKeyExW error: %ld\n", res ); + return FALSE; + } + + RegCloseKey(h_key); + return ( dispos == REG_OPENED_EXISTING_KEY ); +} + +BOOL save_to_reg( WCHAR* path, DWORD reg_type, CHAR* name, const BYTE* value, DWORD size ) +{ + HKEY h_key; + DWORD dispos; + LSTATUS res; + + res = get_info_key(path, &h_key, &dispos); + + if( res != ERROR_SUCCESS ) + { + ERR( "RegCreateKeyExW: %ld\n", res ); + return FALSE; + } + + res = RegSetValueExA( + h_key, + name, + 0, + reg_type, + value, + size + ); + + RegCloseKey( h_key ); + + if ( res != ERROR_SUCCESS ) { + ERR( "RegSetValueExA error: %ld\n", res ); + return FALSE; + } + + return TRUE; +} + +BOOL load_from_reg( WCHAR* path, int opt_type, CHAR* name, void* value ) +{ + HKEY h_key; + DWORD dispos, flag, size; + LSTATUS res; + + res = get_info_key(path, &h_key, &dispos); + + if( res != ERROR_SUCCESS ) + { + ERR( "RegCreateKeyExW: %ld\n", res ); + return FALSE; + } + + switch( opt_type ) + { + case TYPE_INT: + case TYPE_FIXED: + case TYPE_BOOL: + flag = RRF_RT_REG_DWORD; + size = sizeof(DWORD); + break; + case TYPE_STRING: + flag = RRF_RT_REG_SZ; + size = OPTION_VALUE_MAX; + break; + default: + RegCloseKey( h_key ); + ERR( "Unknown type: %d\n", opt_type ); + return FALSE; + } + + res = RegGetValueA( + h_key, + NULL, + name, + flag, + NULL, + value, + &size + ); + + RegCloseKey( h_key ); + + if ( res != ERROR_SUCCESS ) { + ERR( "RegGetValueExA error: %ld\n", res ); + return FALSE; + } + + return TRUE; +} diff --git a/dlls/sane.ds/cfg.h b/dlls/sane.ds/cfg.h new file mode 100644 index 00000000000..9bea843deef --- /dev/null +++ b/dlls/sane.ds/cfg.h @@ -0,0 +1,48 @@ +/* +* TWAIN32 Configuration Manager +* +* Copyright 2025 Ivan Lyugaev +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" + +#include "sane_i.h" + +#define OPTION_VALUE_MAX 255 +#define OPTION_NAME_MAX 64 + +typedef struct +{ + INT opt_type; + DWORD reg_type; + CHAR name[OPTION_NAME_MAX]; + INT optno; + BYTE value[OPTION_VALUE_MAX]; + DWORD size; + BOOL is_enabled; +} ScannerOption; + +BOOL save_to_reg( WCHAR* path, DWORD reg_type, CHAR* name, const BYTE* value, DWORD size ); +BOOL load_from_reg( WCHAR* path, int opt_type, CHAR* name, void* value ); + +BOOL is_exist_reg( WCHAR* path ); diff --git a/dlls/sane.ds/sane_main.c b/dlls/sane.ds/sane_main.c index 8f07ce6d52a..360509e2bf6 100644 --- a/dlls/sane.ds/sane_main.c +++ b/dlls/sane.ds/sane_main.c @@ -74,6 +74,9 @@ static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) activeDS.twCC = SANE_SaneSetDefaults(); if (activeDS.twCC == TWCC_SUCCESS) { + strcpy(activeDS.identity.Manufacturer, self->Manufacturer); + strcpy(activeDS.identity.ProductFamily, self->ProductFamily); + strcpy(activeDS.identity.ProductName, self->ProductName); activeDS.currentState = 4; activeDS.identity.Id = self->Id; activeDS.appIdentity = *pOrigin; diff --git a/dlls/sane.ds/ui.c b/dlls/sane.ds/ui.c index 25a1cf33970..61e2c782a19 100644 --- a/dlls/sane.ds/ui.c +++ b/dlls/sane.ds/ui.c @@ -30,11 +30,16 @@ #include "wine/debug.h" #include "resource.h"
+#include "cfg.h" + WINE_DEFAULT_DEBUG_CHANNEL(twain);
-#define ID_BASE 0x100 -#define ID_EDIT_BASE 0x1000 -#define ID_STATIC_BASE 0x2000 +#define ID_BASE 0x100 +#define ID_EDIT_BASE 0x1000 +#define ID_STATIC_BASE 0x2000 + +WCHAR path[MAX_PATH]; +int gOptCount;
static INT_PTR CALLBACK DialogProc (HWND , UINT , WPARAM , LPARAM ); static INT CALLBACK PropSheetProc(HWND, UINT,LPARAM); @@ -485,6 +490,8 @@ BOOL DoScannerUI(void)
hdc = CreateCompatibleDC(0);
+ gOptCount = optcount; + while (index < optcount) { struct option_descriptor opt; @@ -508,13 +515,26 @@ BOOL DoScannerUI(void) psp[page_count].lParam = (LPARAM)&activeDS; page_count ++; } - + index ++; } - + len = lstrlenA(activeDS.identity.Manufacturer) + lstrlenA(activeDS.identity.ProductName) + 2; szCaption = malloc(len *sizeof(WCHAR)); + + for (int i = 0; activeDS.identity.ProductFamily[i]; i++) + { + if (activeDS.identity.ProductFamily[i] == L'/') + { + activeDS.identity.ProductFamily[i] = L'_'; + } + } + swprintf(path, MAX_PATH, L"%S\%S_%S.sc", + activeDS.identity.Manufacturer, + activeDS.identity.ProductFamily, + activeDS.identity.ProductName); + MultiByteToWideChar(CP_ACP,0,activeDS.identity.Manufacturer,-1, szCaption,len); szCaption[lstrlenA(activeDS.identity.Manufacturer)] = ' '; @@ -548,6 +568,117 @@ BOOL DoScannerUI(void) return FALSE; }
+static void get_option(struct option_descriptor* opt, ScannerOption* option) +{ + CHAR title[OPTION_NAME_MAX]; + WideCharToMultiByte(CP_UTF8, 0, opt->title, -1, title, sizeof(title), NULL, NULL); + lstrcpynA(option->name, title, OPTION_NAME_MAX); + option->is_enabled = opt->is_active; + option->optno = opt->optno; + + if (opt->type ==TYPE_STRING && opt->constraint_type != CONSTRAINT_NONE) + { + CHAR buffer[255]; + option->reg_type = REG_SZ; + option->opt_type = opt->type; + sane_option_get_value(opt->optno, buffer); + lstrcpynA((CHAR*)option->value, buffer, OPTION_VALUE_MAX); + option->size = (DWORD)(strlen(buffer) + 1); + } + else if (opt->type == TYPE_BOOL) + { + BOOL b; + option->opt_type = opt->type; + option->reg_type = REG_DWORD; + sane_option_get_value(opt->optno, &b); + memcpy(option->value, &b, sizeof(BOOL)); + option->size = sizeof(b); + } + else if (opt->type == TYPE_INT && opt->constraint_type == CONSTRAINT_WORD_LIST) + { + int val; + option->opt_type = opt->type; + option->reg_type = REG_DWORD; + sane_option_get_value(opt->optno, &val); + memcpy(option->value, &val, sizeof(INT)); + option->size = sizeof(val); + } + else if (opt->constraint_type == CONSTRAINT_RANGE) + { + if (opt->type == TYPE_INT) + { + int si; + option->opt_type = opt->type; + option->reg_type = REG_DWORD; + sane_option_get_value(opt->optno, &si); + if (opt->constraint.range.quant) + { + si = si / opt->constraint.range.quant; + } + memcpy(option->value, &si, sizeof(INT)); + option->size = sizeof(si); + } + else if (opt->type == TYPE_FIXED) + { + int pos, *sf; + option->opt_type = opt->type; + option->reg_type = REG_DWORD; + sf = calloc( opt->size, sizeof(int) ); + sane_option_get_value(opt->optno, sf ); + if (opt->constraint.range.quant) + pos = *sf / opt->constraint.range.quant; + else + pos = MulDiv( *sf, 100, 65536 ); + memcpy(option->value, &pos, sizeof(INT)); + option->size = sizeof(pos); + free(sf); + } + } +} + +static BOOL save_option(int optno) +{ + ScannerOption option; + struct option_descriptor opt; + + opt.optno = optno; + SANE_CALL( option_get_descriptor, &opt); + + get_option(&opt, &option); + return save_to_reg(path, option.reg_type, option.name, option.value, option.size); +} + +static BOOL save_options(void) +{ + TW_UINT16 rc; + int optcount; + + rc = sane_option_get_value( 0, &optcount ); + if (rc != TWCC_SUCCESS) + { + ERR("Unable to read number of options\n"); + optcount = gOptCount; + } + else + gOptCount = optcount; + + for (int i = 1; i < optcount; i++) + { + ScannerOption option; + struct option_descriptor opt; + opt.optno = i; + SANE_CALL( option_get_descriptor, &opt); + if (!opt.is_active) + continue; + get_option(&opt, &option); + if (!save_to_reg(path, option.reg_type, option.name, option.value, option.size)) + { + ERR("Option %s could not be saved to the registry!", option.name); + } + } + return TRUE; +} + static void UpdateRelevantEdit(HWND hwnd, const struct option_descriptor *opt, int position) { WCHAR buffer[244]; @@ -607,6 +738,7 @@ static BOOL UpdateSaneScrollOption(const struct option_descriptor *opt, DWORD po si = position;
sane_option_set_value( opt->optno, &si, &result ); + save_option(opt->optno); break; } case TYPE_FIXED: @@ -616,6 +748,7 @@ static BOOL UpdateSaneScrollOption(const struct option_descriptor *opt, DWORD po si = MulDiv( position, 65536, 100 );
sane_option_set_value( opt->optno, &si, &result ); + save_option(opt->optno); break; default: break; @@ -635,19 +768,27 @@ static INT_PTR InitializeDialog(HWND hwnd) if (rc != TWCC_SUCCESS) { ERR("Unable to read number of options\n"); - return FALSE; + optcount = gOptCount; + } + else + gOptCount = optcount; + + if ( !is_exist_reg(path) ) + { + save_options(); }
for ( i = 1; i < optcount; i++) { + CHAR title[64]; struct option_descriptor opt; - control = GetDlgItem(hwnd,i+ID_BASE);
if (!control) continue;
opt.optno = i; + SANE_CALL( option_get_descriptor, &opt );
TRACE("%i %s %i %i\n",i,debugstr_w(opt.title),opt.type,opt.constraint_type); @@ -655,34 +796,77 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,CB_RESETCONTENT,0,0); /* initialize values */ + + WideCharToMultiByte(CP_UTF8, 0, opt.title, -1, title, sizeof(title), NULL, NULL); + if (opt.type == TYPE_STRING && opt.constraint_type != CONSTRAINT_NONE) { CHAR buffer[255]; WCHAR *p;
+ BOOL is_exist = load_from_reg(path, opt.type, title, buffer); + BOOL is_correct = FALSE; + for (p = opt.constraint.strings; *p; p += lstrlenW(p) + 1) + { + CHAR param[256]; SendMessageW( control,CB_ADDSTRING,0, (LPARAM)p ); + WideCharToMultiByte(CP_UTF8, 0, p, -1, param, sizeof(param), NULL, NULL); + if (is_exist && !strcmp(param, buffer)) + { + is_correct = TRUE; + } + } + + if (is_exist && is_correct) + { + sane_option_set_value(opt.optno, buffer, NULL); + } + + if (!is_correct) + { + ERR("%s=%s is incorrect. The default value is set!", title, buffer); + } + sane_option_get_value( i, buffer ); SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); } else if (opt.type == TYPE_BOOL) { BOOL b; - sane_option_get_value( i, &b ); - if (b) - SendMessageA(control,BM_SETCHECK,BST_CHECKED,0); + BOOL is_exist = load_from_reg(path, opt.type, title, &b); + + if (is_exist) + { + sane_option_set_value( i, &b, NULL ); + }
+ sane_option_get_value( i, &b ); + SendMessageA(control,BM_SETCHECK, b ? BST_CHECKED : BST_UNCHECKED,0); } else if (opt.type == TYPE_INT && opt.constraint_type == CONSTRAINT_WORD_LIST) { int j, count = opt.constraint.word_list[0]; CHAR buffer[16]; int val; + BOOL is_exist = load_from_reg(path, opt.type, title, &val); + BOOL is_correct = FALSE; + for (j=1; j<=count; j++) { + if (opt.constraint.word_list[j] == val) + { + is_correct = TRUE; + } sprintf(buffer, "%d", opt.constraint.word_list[j]); SendMessageA(control, CB_ADDSTRING, 0, (LPARAM)buffer); } + if (is_exist && is_correct) + sane_option_set_value( i, &val, NULL ); + + if (!is_correct) + ERR("%s=%d is incorrect. The default value is set!\n", title, val); + sane_option_get_value( i, &val ); sprintf(buffer, "%d", val); SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); @@ -693,6 +877,8 @@ static INT_PTR InitializeDialog(HWND hwnd) { int si; int min,max; + BOOL is_exist = load_from_reg(path, opt.type, title, &si); + BOOL is_correct = FALSE;
min = opt.constraint.range.min / (opt.constraint.range.quant ? opt.constraint.range.quant : 1); @@ -702,7 +888,16 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,SBM_SETRANGE,min,max);
+ if (si >= min && si <= max) + is_correct = TRUE; + else + ERR("%s=%d is out of range [%d..%d]. The default value is used!\n", title, si, min, max); + + if (is_correct && is_exist) + sane_option_set_value( i, &si, NULL); + sane_option_get_value( i, &si ); + if (opt.constraint.range.quant) si = si / opt.constraint.range.quant;
@@ -711,8 +906,8 @@ static INT_PTR InitializeDialog(HWND hwnd) } else if (opt.type == TYPE_FIXED) { - int pos, min, max, *sf; - + int pos, min, max, *sf, val; + BOOL is_exist, is_correct = FALSE; if (opt.constraint.range.quant) { min = opt.constraint.range.min / opt.constraint.range.quant; @@ -726,6 +921,23 @@ static INT_PTR InitializeDialog(HWND hwnd)
SendMessageA(control,SBM_SETRANGE,min,max);
+ is_exist = load_from_reg(path, opt.type, title, &val); + + if (val >= min && val <= max) + is_correct = TRUE; + else + ERR("%s = %d is out of range [%d..%d]. The default value is used!\n", title, val, min, max); + + if (is_exist && is_correct) + { + int valSet; + if (opt.constraint.range.quant) + valSet = val * opt.constraint.range.quant; + else + valSet = MulDiv(val, 65536, 100); + sane_option_set_value(i, &valSet, NULL); + } +
sf = calloc( opt.size, sizeof(int) ); sane_option_get_value( i, sf ); @@ -817,7 +1029,12 @@ static void ButtonClicked(HWND hwnd, INT id, HWND control) { BOOL r = SendMessageW(control,BM_GETCHECK,0,0)==BST_CHECKED; sane_option_set_value( opt.optno, &r, &changed ); - if (changed) InitializeDialog(hwnd); + + if (changed) + { + save_option(opt.optno); + InitializeDialog(hwnd); + } } }
@@ -851,11 +1068,15 @@ static void ComboChanged(HWND hwnd, INT id, HWND control) int val = atoi( value ); sane_option_set_value( opt.optno, &val, &changed ); } - if (changed) InitializeDialog(hwnd); + + if (changed) + { + save_option(opt.optno); + InitializeDialog(hwnd); + } free( value ); }
- static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) @@ -907,7 +1128,8 @@ static int CALLBACK PropSheetProc(HWND hwnd, UINT msg, LPARAM lParam) HWND scan = GetDlgItem(hwnd,IDOK); SetWindowTextA(scan,"Scan"); } - return TRUE; + + return TRUE; }
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
if (rc != TWCC_SUCCESS) { ERR("Unable to read number of options\n");
return FALSE;
optcount = gOptCount;
- }
- else
gOptCount = optcount;
- if ( !is_exist_reg(path) )
- {
}save_options();
Aren't these just the defaults at this point? If so, why save them?
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
return FALSE;
}
+static void get_option(struct option_descriptor* opt, ScannerOption* option) +{
- CHAR title[OPTION_NAME_MAX];
- WideCharToMultiByte(CP_UTF8, 0, opt->title, -1, title, sizeof(title), NULL, NULL);
- lstrcpynA(option->name, title, OPTION_NAME_MAX);
As I understand it, title is meant to be a UI string (potentially localized) so I wouldn't rely on it to be consistent. I also worry about encoding and syntax issues. I suggest using the option name: https://sane-project.gitlab.io/standard/api.html#option-name
That will require updating the unixlib interface to include this in `struct option_descriptor` and copy it in `map_descr`.
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
len = lstrlenA(activeDS.identity.Manufacturer) + lstrlenA(activeDS.identity.ProductName) + 2; szCaption = malloc(len *sizeof(WCHAR));
- for (int i = 0; activeDS.identity.ProductFamily[i]; i++)
- {
if (activeDS.identity.ProductFamily[i] == L'/')
{
activeDS.identity.ProductFamily[i] = L'_';
}
- }
I'm not sure if it's safe to modify the TW_IDENTITY structure in this way. We should probably make a copy (or just allow slashes in the key name, it seems like the registry doesn't mind them).
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
return FALSE;
}
+static void get_option(struct option_descriptor* opt, ScannerOption* option) +{
- CHAR title[OPTION_NAME_MAX];
- WideCharToMultiByte(CP_UTF8, 0, opt->title, -1, title, sizeof(title), NULL, NULL);
- lstrcpynA(option->name, title, OPTION_NAME_MAX);
- option->is_enabled = opt->is_active;
- option->optno = opt->optno;
- if (opt->type ==TYPE_STRING && opt->constraint_type != CONSTRAINT_NONE)
Why do we check the constraint type here?
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
else if (opt->type == TYPE_FIXED)
{
int pos, *sf;
option->opt_type = opt->type;
option->reg_type = REG_DWORD;
sf = calloc( opt->size, sizeof(int) );
sane_option_get_value(opt->optno, sf );
if (opt->constraint.range.quant)
pos = *sf / opt->constraint.range.quant;
else
pos = MulDiv( *sf, 100, 65536 );
memcpy(option->value, &pos, sizeof(INT));
option->size = sizeof(pos);
free(sf);
}
- }
I'm not sure if this covers all potential option type+constraint combinations. We should probably print a FIXME if we get something unexpected, and maybe initialize these fields to something invalid?
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
WideCharToMultiByte(CP_UTF8, 0, p, -1, param, sizeof(param), NULL, NULL);
if (is_exist && !strcmp(param, buffer))
{
is_correct = TRUE;
}
}
if (is_exist && is_correct)
{
sane_option_set_value(opt.optno, buffer, NULL);
}
if (!is_correct)
{
ERR("%s=%s is incorrect. The default value is set!", title, buffer);
}
We probably shouldn't print this if the setting doesn't exist (and I think it might use buffer uninitialized in that case).
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
BOOL is_correct = FALSE;
for (j=1; j<=count; j++) {
if (opt.constraint.word_list[j] == val)
{
is_correct = TRUE;
} sprintf(buffer, "%d", opt.constraint.word_list[j]); SendMessageA(control, CB_ADDSTRING, 0, (LPARAM)buffer); }
if (is_exist && is_correct)
sane_option_set_value( i, &val, NULL );
if (!is_correct)
ERR("%s=%d is incorrect. The default value is set!\n", title, val);
I don't think we should print this if the setting doesn't exist.
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
SendMessageA(control,SBM_SETRANGE,min,max);
if (si >= min && si <= max)
I don't think we should check this if the setting doesn't exist.
Esme Povirk (@madewokherd) commented about dlls/sane.ds/ui.c:
SendMessageA(control,SBM_SETRANGE,min,max);
is_exist = load_from_reg(path, opt.type, title, &val);
if (val >= min && val <= max)
I don't think we should check this if the setting doesn't exist.
On Tue Sep 2 22:03:46 2025 +0000, Esme Povirk wrote:
I'm not sure if this covers all potential option type+constraint combinations. We should probably print a FIXME if we get something unexpected, and maybe initialize these fields to something invalid?
This block repeats the initialization of the parameters in the InitializeDialog function. I have saved only the necessary operations to generate the parameters.
` else if (opt.type == TYPE_FIXED) { int pos, min, max, *sf;
if (opt.constraint.range.quant) { min = opt.constraint.range.min / opt.constraint.range.quant; max = opt.constraint.range.max / opt.constraint.range.quant; } else { min = MulDiv( opt.constraint.range.min, 100, 65536 ); max = MulDiv( opt.constraint.range.max, 100, 65536 ); }
SendMessageA(control,SBM_SETRANGE,min,max);
sf = calloc( opt.size, sizeof(int) ); sane_option_get_value( i, sf );
/* Note that conversion of float -> SANE_Fixed is lossy; * and when you truncate it into an integer, you can get * unfortunate results. This calculation attempts * to mitigate that harm */ if (opt.constraint.range.quant) pos = *sf / opt.constraint.range.quant; else pos = MulDiv( *sf, 100, 65536 );
free(sf); SendMessageW(control, SBM_SETPOS, pos, TRUE); UpdateRelevantEdit(hwnd, &opt, pos); } `
On Tue Sep 2 22:03:46 2025 +0000, Esme Povirk wrote:
Why do we check the constraint type here?
This block repeats the initialization of the parameters in the InitializeDialog function. I have saved only the necessary operations to generate the parameters.
`
/* initialize values */ if (opt.type == TYPE_STRING && opt.constraint_type != CONSTRAINT_NONE) { CHAR buffer[255]; WCHAR *p;
for (p = opt.constraint.strings; *p; p += lstrlenW(p) + 1) SendMessageW( control,CB_ADDSTRING,0, (LPARAM)p ); sane_option_get_value( i, buffer ); SendMessageA(control,CB_SELECTSTRING,0,(LPARAM)buffer); }
`
On Wed Sep 3 06:00:53 2025 +0000, Esme Povirk wrote:
Aren't these just the defaults at this point? If so, why save them?
You're right, the printer initially uses the default settings (sane settings). But it is important to note here that this fragment is necessary at the first contact with the scanner for the initial initialization of the registry. If the registry key does not exist, it is first initialized with the current (default) settings, and then changes are made to it.
On Wed Sep 3 07:17:45 2025 +0000, Esme Povirk wrote:
We probably shouldn't print this if the setting doesn't exist (and I think it might use buffer uninitialized in that case).
I added the is_exist flag to the condition. The purpose of this message is to inform the user that the registry contains an invalid parameter that is not supported by sane. This is a protection against manual parameter changes by the user
On Wed Sep 3 07:17:29 2025 +0000, Esme Povirk wrote:
I don't think we should check this if the setting doesn't exist.
Added the is_exist flag
On Wed Sep 3 07:17:27 2025 +0000, Esme Povirk wrote:
I don't think we should check this if the setting doesn't exist.
Added the is_exist flag
On Wed Sep 3 07:17:41 2025 +0000, Esme Povirk wrote:
I don't think we should print this if the setting doesn't exist.
Added the is_exist flag