summaryrefslogblamecommitdiffstats
path: root/windows.c
blob: 823615c4a82efadb4ab09b8896360b23aa43d24c (plain) (tree)
1
2
3
4
5
6
7
8
9
                                           
                 
                    
                        
      
                    
                   
                                                     

                 


                               
                                                                                          
                                                                                   
                                                                               
 
                                            
 
                                            
 
                                                             
 




                                                                      
                 
                                 


                                                                       




                                                                                  
         

                                                                              
                    
 
                                                         
 
                                                                                  
 
                                                          
 
                                             
 
                                                 
 

                                   
                                        
                          
 



                                                                                  
                                        





                                                                              
                            
                              
         











                                                                     
                                             
 
                                  
                                                                         
                                                                
                                 
                     
 
                                              
 
                                                
                           
                        
 
                                
 
                             


                                
                          
 
              
                                                    
 

                                                   
                         




































                                                                                                      
                                                                                                       
                                                                                    
                                                                                              






                                                                                       
                                                        





                                                            
                     
 
                       



                                  
 
                                         
 


                                
 


                                                                                   
         


                                                                        
 
                                                      
 
                                                                                      
                        
                                                                      
                       
                       
                                                                                                
                                
 
              
                                                                   
                                                          
 
                                                                    
                       





                                                                        
                                                              




                                                                        





                                                                             






































                                                                                                 
 
                                                   








                                                                                                
 












                                                                   
















                                                                                                                  
                                     
























                                                                                                    
                                                          




                                  
/* windows.c */
/* implements Windows specific functions */
#include "dive.h"
#include "display.h"
#if USE_GTK_UI
#include "display-gtk.h"
#endif
#include <windows.h>
#include <shlobj.h>

const char system_divelist_default_font[] = "Sans 8";

static HKEY hkey;

void subsurface_open_conf(void)
{
	LONG success;

	success = RegCreateKeyEx(HKEY_CURRENT_USER, (LPCTSTR)TEXT("Software\\subsurface"),
	                         0L, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
	                         NULL, &hkey, NULL);
	if (success != ERROR_SUCCESS)
		printf("CreateKey Software\\subsurface failed %ld\n", success);
}

void subsurface_unset_conf(const char *name)
{
	RegDeleteValue(hkey, (LPCTSTR)name);
}

void subsurface_set_conf(const char *name, const char *value)
{
	/* since we are using the pointer 'value' as both an actual
	 * pointer to the string setting and as a way to pass the
	 * numbers 0 and 1 to this function for booleans, one of the
	 * calls to RegSetValueEx needs to pass &value (when we want
	 * to pass the boolean value), the other one passes value (the
	 * address of the string. */
	int wlen;
	wchar_t *wname, *wstring;

	wname = (wchar_t *)g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
	if (!wname)
		return;

	wlen = g_utf8_strlen((char *)value, -1);
	wstring = (wchar_t *)g_utf8_to_utf16((char *)value, -1, NULL, NULL, NULL);
	if (!wstring || !wlen) {
		free(wname);
		return;
	}
	RegSetValueExW(hkey, (LPCWSTR)wname, 0, REG_SZ, (const BYTE *)wstring,
	               wlen * sizeof(wchar_t));
	free(wstring);
	free(wname);
}

void subsurface_set_conf_int(const char *name, int value)
{
	RegSetValueEx(hkey, (LPCTSTR)name, 0, REG_DWORD, (const BYTE *)&value, 4);
}

void subsurface_set_conf_bool(const char *name, int value)
{
	subsurface_set_conf_int(name, value);
}

const char *subsurface_get_conf(const char *name)
{
	const int csize = 64;
	int blen = 0;
	LONG ret = ERROR_MORE_DATA;
	wchar_t *wstring = NULL, *wname;
	char *utf8_string;

	wname = (wchar_t *)g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
	if (!wname)
		return NULL;
	blen = 0;
	/* lest try to load the string in chunks of 'csize' bytes until it fits */
	while (ret == ERROR_MORE_DATA) {
		blen += csize;
		wstring = (wchar_t *)realloc(wstring, blen * sizeof(wchar_t));
		ret = RegQueryValueExW(hkey, (LPCWSTR)wname, NULL, NULL,
		                     (LPBYTE)wstring, (LPDWORD)&blen);
	}
	/* that's what happens the first time we start - just return NULL */
	if (ret != ERROR_SUCCESS) {
		free(wname);
		free(wstring);
		return NULL;
	}
	/* convert the returned string into utf-8 */
	utf8_string = g_utf16_to_utf8(wstring, -1, NULL, NULL, NULL);
	free(wstring);
	free(wname);
	if (!utf8_string)
		return NULL;
	if (!g_utf8_validate(utf8_string, -1, NULL)) {
		free(utf8_string);
		return NULL;
	}
	return utf8_string;
}

int subsurface_get_conf_int(const char *name)
{
	DWORD value = -1, len = 4;
	LONG ret = RegQueryValueEx(hkey, (LPCTSTR)TEXT(name), NULL, NULL,
	                         (LPBYTE)&value, (LPDWORD)&len);
	if (ret != ERROR_SUCCESS)
		return -1;
	return value;
}

int subsurface_get_conf_bool(const char *name)
{
	int ret = subsurface_get_conf_int(name);
	if (ret == -1)
		return ret;
	return ret != 0;
}

void subsurface_flush_conf(void)
{
	/* this is a no-op */
}

void subsurface_close_conf(void)
{
	RegCloseKey(hkey);
}

#if USE_GTK_UI
int subsurface_fill_device_list(GtkListStore *store)
{
	const int bufdef = 512;
	const char *dlabels[] = {"UEMISSDA", NULL};
	const char *devdef = "COM1";
	GtkTreeIter iter;
	int index = -1, nentries = 0, ret, i;
	char bufname[bufdef], bufval[bufdef], *p;
	DWORD nvalues, bufval_len, bufname_len;
	HKEY key;

	/* add serial ports */
	ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM",
	                    0, KEY_READ, &key);
	if (ret == ERROR_SUCCESS) {
		ret = RegQueryInfoKeyA(key,  NULL, NULL, NULL, NULL, NULL, NULL, &nvalues,
		                       NULL, NULL, NULL, NULL);
		if (ret == ERROR_SUCCESS)
			for (i = 0; i < nvalues; i++) {
				memset(bufval, 0, bufdef);
				memset(bufname, 0, bufdef);
				bufname_len = bufdef;
				bufval_len = bufdef;
				ret = RegEnumValueA(key, i, bufname, &bufname_len, NULL, NULL, bufval,
				                    &bufval_len);
				if (ret == ERROR_SUCCESS) {
					gtk_list_store_append(store, &iter);
					gtk_list_store_set(store, &iter, 0, bufval, -1);
					if (is_default_dive_computer_device(bufval))
						index = nentries;
					nentries++;
				}
			}
	}
	/* add drive letters that match labels */
	memset(bufname, 0, bufdef);
	bufname_len = bufdef;
	if (GetLogicalDriveStringsA(bufname_len, bufname)) {
		p = bufname;
		while (*p) {
			memset(bufval, 0, bufdef);
			if (GetVolumeInformationA(p, bufval, bufdef, NULL, NULL, NULL, NULL, 0)) {
				for (i = 0; dlabels[i] != NULL; i++)
					if (!strcmp(bufval, dlabels[i])) {
						char name[80];
						snprintf(name, sizeof(name), "%s (%s)", p, dlabels[i]);
						gtk_list_store_append(store, &iter);
						gtk_list_store_set(store, &iter, 0, name, -1);
						if (is_default_dive_computer_device(p))
							index = nentries;
						nentries++;
					}
			}
			p = &p[strlen(p) + 1];
		}
	}
	/* if we can't find anything, use the default */
	if (!nentries) {
		gtk_list_store_append(store, &iter);
		gtk_list_store_set(store, &iter,
				0, devdef, -1);
		if (is_default_dive_computer_device(devdef))
			index = 0;
	}
	return index;
}
#endif /* USE_GTK_UI */

const char *subsurface_icon_name()
{
	return "subsurface.ico";
}

const char *system_default_filename(void)
{
	char datapath[MAX_PATH];
	const char *user;
	char *buffer;
	int len;

	user = g_get_user_name();
	if (! SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, datapath))) {
		datapath[0] = '.';
		datapath[1] = '\0';
	}
	len = strlen(datapath) + strlen(user) + 17;
	buffer = malloc(len);
	snprintf(buffer, len, "%s\\Subsurface\\%s.xml", datapath, user);
	return buffer;
}

const char *subsurface_gettext_domainpath(char *argv0)
{
	/* first hackishly make sure that the LANGUAGE information is correctly set up
	 * in the environment */
	char buffer[80];
	gchar *locale = g_win32_getlocale();
	snprintf(buffer, sizeof(buffer), "LANGUAGE=%s.UTF-8", locale);
	putenv(buffer);
	g_free(locale);
	/* always use translation directory relative to install location, regardless of argv0 */
	return "./share/locale";
}

#if USE_GTK_UI
void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
		GtkWidget *vbox, GtkUIManager *ui_manager)
{
	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
}
#endif /* USE_GTK_UI */

/* barely documented API */
extern int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);

/* expand-convert the UTF-16 argument list to a list of UTF-8 strings */
void subsurface_command_line_init(gint *argc, gchar ***argv)
{
	wchar_t **wargv, **wenviron, *p, path[MAX_PATH] = {0};
	gchar **argv_new;
	gchar *s;
	/* for si we assume that a struct address will equal the address
	 * of its first and only int member */
	gint i, n, ret, si;

	/* change the current process path to the module path, so that we can
	 * access relative folders such as ./share and ./xslt */
	GetModuleFileNameW(NULL, path, MAX_PATH - 1);
	p = wcsrchr(path, '\\');
	*(p + 1) = '\0';
	SetCurrentDirectoryW(path);

	/* memory leak tools may reports a potential issue here at a call
	 * to strcpy_s in msvcrt, wich should be a false positive. but even if there
	 * is some kind of a leak, it should be unique and have the same
	 * lifespan as the process heap. */
	ret = __wgetmainargs(&n, &wargv, &wenviron, TRUE, &si);
	if (ret < 0) {
		g_warning("Cannot convert command line");
		return;
	}
	argv_new = g_malloc(sizeof(gchar *) * (n + 1));

	for (i = 0; i < n; ++i) {
		s = g_utf16_to_utf8((gunichar2 *)wargv[i], -1, NULL, NULL, NULL);
		if (!s) {
			g_warning("Cannot convert command line argument (%d) to UTF-8", (i + 1));
			s = "\0";
		}	else if (!g_utf8_validate(s, -1, NULL)) {
			g_warning("Cannot validate command line argument '%s' (%d)", s, (i + 1));
			g_free(s);
			s = "\0";
		}
		argv_new[i] = s;
	}
	argv_new[n] = NULL;

	/* update the argument list and count */
	if (argv && argc) {
		*argv = argv_new;
		*argc = n;
	}
}

/* once done, free the argument list */
void subsurface_command_line_exit(gint *argc, gchar ***argv)
{
	int i;
	for (i = 0; i < *argc; i++)
		g_free((*argv)[i]);
	g_free(*argv);
}

gboolean subsurface_launch_for_uri(const char* uri)
{
	gboolean ret = FALSE;
	wchar_t *wuri = (wchar_t *)g_utf8_to_utf16(uri, -1, NULL, NULL, NULL);
	if (wuri) {
		if ((INT_PTR)ShellExecuteW(NULL, L"open", wuri, NULL, NULL, SW_SHOWNORMAL) > 32)
			ret = TRUE;
		free(wuri);
	}
	if (!ret)
		g_message("ShellExecute failed for: %s", uri);
	return ret;
}

/* check if we are running a newer OS version */
gboolean subsurface_os_feature_available(os_feature_t f)
{
	switch (f) {
	case UTF8_FONT_WITH_STARS:
		if ((GetVersion() & 0xff) < 6)
			return FALSE; /* version less than Vista */
		else
			return TRUE;
		break;
	default:
		return TRUE;
	}
}

int enumerate_devices (device_callback_t callback, void *userdata)
{
	// Open the registry key.
	HKEY hKey;
	int index = -1;
	LONG rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey);
	if (rc != ERROR_SUCCESS) {
		return -1;
	}

	// Get the number of values.
	DWORD count = 0;
	rc = RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL);
	if (rc != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		return -1;
	}
	DWORD i;
	for (i = 0; i < count; ++i) {
		// Get the value name, data and type.
		char name[512], data[512];
		DWORD name_len = sizeof (name);
		DWORD data_len = sizeof (data);
		DWORD type = 0;
		rc = RegEnumValue (hKey, i, name, &name_len, NULL, &type, (LPBYTE) data, &data_len);
		if (rc != ERROR_SUCCESS) {
			RegCloseKey(hKey);
			return -1;
		}

		// Ignore non-string values.
		if (type != REG_SZ)
			continue;

		// Prevent a possible buffer overflow.
		if (data_len >= sizeof (data)) {
			RegCloseKey(hKey);
			return -1;
		}

		// Null terminate the string.
		data[data_len] = 0;

		callback (data, userdata);
		index++;
		if (is_default_dive_computer_device(name))
			index = i;
	}

	RegCloseKey(hKey);
	return index;
}