mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-13 12:19:25 +00:00
470 lines
10 KiB
C
470 lines
10 KiB
C
/* kmfl_load_keyboard.c
|
|
* Copyright (C) 2005 SIL International
|
|
*
|
|
* This file is part of the KMFL library.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
|
|
Keystroke interpreter for keyboard mapping for Linux project
|
|
|
|
Version 1.000, January 2004, John Durdin, Tavultesoft
|
|
|
|
Keyboard and loading and instance management
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <setjmp.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <kmfl/kmfl.h>
|
|
#include <kmfl/kmflcomp.h>
|
|
#include <kmfl/kmflutfconv.h>
|
|
#include "libkmfl.h"
|
|
|
|
// Globally loaded keyboards and instances
|
|
XKEYBOARD *p_installed_kbd[MAX_KEYBOARDS]={NULL};
|
|
char * keyboard_filename[MAX_KEYBOARDS];
|
|
|
|
KMSI *p_first_instance={NULL};
|
|
unsigned int n_keyboards=0;
|
|
|
|
// Create a new keyboard mapping server instance
|
|
KMSI *kmfl_make_keyboard_instance(void *connection)
|
|
{
|
|
KMSI *p_kmsi, *p;
|
|
|
|
if((p_kmsi=(KMSI *)malloc(sizeof(KMSI))))
|
|
{
|
|
p_kmsi->history = (ITEM *)malloc((MAX_HISTORY+2)*sizeof(ITEM));
|
|
if(p_kmsi->history)
|
|
{
|
|
p_kmsi->connection = connection;
|
|
*p_kmsi->kbd_name = 0;
|
|
p_kmsi->keyboard = NULL;
|
|
p_kmsi->groups = NULL;
|
|
p_kmsi->rules = NULL;
|
|
p_kmsi->stores = NULL;
|
|
p_kmsi->strings = NULL;
|
|
p_kmsi->nhistory = 0;
|
|
|
|
// Link to other keyboard instances
|
|
if(p_first_instance == NULL)
|
|
{
|
|
p_first_instance = p_kmsi;
|
|
p_kmsi->last = NULL;
|
|
}
|
|
else
|
|
{
|
|
for(p=p_first_instance; p->next!=NULL; p=p->next);
|
|
p->next = p_kmsi;
|
|
p_kmsi->last = p;
|
|
}
|
|
p_kmsi->next = NULL;
|
|
DBGMSG(1,"Keyboard instance created\n");
|
|
return p_kmsi;
|
|
}
|
|
else
|
|
{
|
|
free(p_kmsi);
|
|
}
|
|
}
|
|
DBGMSG(1,"Unable to create keyboard instance!\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Delete a keyboard mapping server instance
|
|
int kmfl_delete_keyboard_instance(KMSI *p_kmsi)
|
|
{
|
|
KMSI *p1, *p2;
|
|
|
|
// Remove this instance from chain of linked instances
|
|
p1 = p_kmsi->last; p2 = p_kmsi->next;
|
|
if(p1) p1->next = p2; else p_first_instance = p2;
|
|
if(p2) p2->last = p1;
|
|
|
|
// Free allocated memory
|
|
if(p_kmsi->history) free(p_kmsi->history);
|
|
free(p_kmsi);
|
|
|
|
DBGMSG(1,"Keyboard instance deleted\n");
|
|
return 0;
|
|
}
|
|
|
|
// Delete all server instances
|
|
int kmfl_delete_all_keyboard_instances(void)
|
|
{
|
|
KMSI *p, *p1;
|
|
for(p=p_first_instance; p!=NULL; p=p1)
|
|
{
|
|
p1 = p->next;
|
|
kmfl_delete_keyboard_instance(p);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Attach a keyboard to a server instance
|
|
int kmfl_attach_keyboard(KMSI *p_kmsi, int keyboard_number)
|
|
{
|
|
XKEYBOARD *p_kbd=NULL;
|
|
XGROUP *gp;
|
|
unsigned int n, nrules;
|
|
|
|
if (p_installed_kbd[keyboard_number] == NULL) {
|
|
DBGMSG(1,"Invalid keyboard number\n");
|
|
return -1;
|
|
}
|
|
|
|
p_kbd=p_installed_kbd[keyboard_number];
|
|
p_kmsi->keyboard = p_kbd;
|
|
p_kmsi->keyboard_number = keyboard_number;
|
|
|
|
// Fill group, rule, store and string pointers
|
|
p_kmsi->stores = (XSTORE *)(p_kbd+1);
|
|
p_kmsi->groups = (XGROUP *)(p_kmsi->stores+p_kbd->nstores);
|
|
p_kmsi->rules = (XRULE *)(p_kmsi->groups+p_kbd->ngroups);
|
|
|
|
for(n=nrules=0,gp=p_kmsi->groups; n<p_kbd->ngroups; n++, gp++)
|
|
{
|
|
nrules += gp->nrules;
|
|
}
|
|
|
|
p_kmsi->strings = (ITEM *)(p_kmsi->rules+nrules);
|
|
|
|
// Initialize history unless keyboard hasn't changed
|
|
if(strcmp(p_kbd->name,p_kmsi->kbd_name) != 0)
|
|
{
|
|
strncpy(p_kmsi->kbd_name,p_kbd->name, NAMELEN);
|
|
p_kmsi->kbd_name[NAMELEN]=0;
|
|
*p_kmsi->history = 0;
|
|
p_kmsi->nhistory = 0;
|
|
}
|
|
|
|
DBGMSG(1,"Keyboard %s attached\n",p_kbd->name);
|
|
return 0;
|
|
}
|
|
|
|
// Detach a keyboard from a server instance
|
|
int kmfl_detach_keyboard(KMSI *p_kmsi)
|
|
{
|
|
if(p_kmsi)
|
|
DBGMSG(1,"Keyboard %s detached\n",p_kmsi->kbd_name);
|
|
|
|
*p_kmsi->kbd_name = 0;
|
|
p_kmsi->keyboard = NULL;
|
|
p_kmsi->groups = NULL;
|
|
p_kmsi->rules = NULL;
|
|
p_kmsi->stores = NULL;
|
|
p_kmsi->strings = NULL;
|
|
return 0;
|
|
}
|
|
|
|
XKEYBOARD * kmfl_load_keyboard_from_file(const char *filename)
|
|
{
|
|
XKEYBOARD *p_kbd;
|
|
FILE *fp;
|
|
char version_string[6]={0};
|
|
unsigned int filelen, kbver=0;
|
|
struct stat fstat;
|
|
const char * extension;
|
|
int errcode;
|
|
|
|
DBGMSG(1,"DAR: kmfl_load_keyboard_from_file %s\n",filename);
|
|
|
|
extension = strrchr(filename, '.');
|
|
|
|
if (extension && (strcmp(extension, ".kmn") == 0))
|
|
{
|
|
|
|
errcode = setjmp(fatal_error_buf);
|
|
|
|
if (errcode == 0)
|
|
{
|
|
compile_keyboard_to_buffer(filename, (void *) &p_kbd);
|
|
memcpy(version_string,p_kbd->version,4); // Copy to ensure terminated
|
|
kbver = (unsigned)atoi(version_string);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the file size
|
|
if(stat(filename,&fstat) != 0)
|
|
return NULL;
|
|
filelen = fstat.st_size;
|
|
|
|
// Allocate memory for the installed keyboard
|
|
if((p_kbd=(XKEYBOARD *)malloc(filelen)) == NULL)
|
|
return NULL;
|
|
|
|
// Open the file
|
|
if((fp=fopen(filename,"rb")) != NULL)
|
|
{
|
|
if (fread(p_kbd, 1, filelen, fp) != filelen)
|
|
{
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
fclose(fp);
|
|
memcpy(version_string,p_kbd->version,4); // Copy to ensure terminated
|
|
kbver = (unsigned)atoi(version_string);
|
|
}
|
|
}
|
|
// Check the loaded file is valid and has the correct version
|
|
if((memcmp(p_kbd->id,"KMFL",4) != 0)
|
|
|| (p_kbd->version[4] != *FILE_VERSION)
|
|
|| (kbver < (unsigned)atoi(BASE_VERSION))
|
|
|| (kbver > (unsigned)atoi(LAST_VERSION)))
|
|
{
|
|
DBGMSG(1, "Invalid version %s\n", version_string);
|
|
free(p_kbd);
|
|
return NULL;
|
|
}
|
|
|
|
DBGMSG(1,"DAR: kmfl_load_keyboard_from_file - %s loaded\n",filename);
|
|
|
|
return p_kbd;
|
|
}
|
|
|
|
// Load the keyboard table into memory and assign it to an empty keyboard slot
|
|
int kmfl_load_keyboard(const char *file)
|
|
{
|
|
XKEYBOARD *p_kbd;
|
|
int keyboard_number;
|
|
|
|
// Check number of installed keyboards
|
|
if(n_keyboards >= MAX_KEYBOARDS) return -1;
|
|
|
|
// initialize the installed keyboards array
|
|
if(n_keyboards == 0)
|
|
memset(p_installed_kbd, 0, sizeof(XKEYBOARD *) * MAX_KEYBOARDS);
|
|
|
|
p_kbd = kmfl_load_keyboard_from_file(file);
|
|
|
|
if (p_kbd == NULL)
|
|
return -1;
|
|
|
|
// Find an empty slot
|
|
for (keyboard_number=0;keyboard_number < MAX_KEYBOARDS; keyboard_number++)
|
|
if (p_installed_kbd[keyboard_number] == NULL)
|
|
break;
|
|
|
|
// Sanity check
|
|
if (keyboard_number == MAX_KEYBOARDS) {
|
|
DBGMSG(1, "Could not find an empty keyboard slot even though there was supposed to be one\n");
|
|
free(p_kbd);
|
|
return -1;
|
|
}
|
|
|
|
// Copy pointer and increment number of installed keyboards
|
|
p_installed_kbd[keyboard_number] = p_kbd;
|
|
keyboard_filename[keyboard_number]=strdup(file);
|
|
|
|
n_keyboards++;
|
|
DBGMSG(1,"Keyboard %s loaded\n",p_kbd->name);
|
|
|
|
return keyboard_number;
|
|
}
|
|
|
|
// Check that a keyboard file is valid
|
|
int kmfl_check_keyboard(const char *file)
|
|
{
|
|
XKEYBOARD xkb;
|
|
FILE *fp;
|
|
char version_string[6]={0};
|
|
unsigned int kbver=0;
|
|
|
|
// Open the file
|
|
if((fp=fopen(file,"rb")) == NULL)
|
|
return(-1);
|
|
|
|
if (fread(&xkb, 1, sizeof(XKEYBOARD), fp) != sizeof(XKEYBOARD))
|
|
{
|
|
fclose(fp);
|
|
return(-1);
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
memcpy(version_string,xkb.version,3); // Copy to ensure terminated
|
|
kbver = (unsigned)atoi(version_string);
|
|
|
|
// Check the loaded file is valid and has the correct version
|
|
if(memcmp(xkb.id,"KMFL",4) != 0)
|
|
return(-2);
|
|
if(xkb.version[3] != *FILE_VERSION)
|
|
return(-2);
|
|
if(kbver < (unsigned)atoi(BASE_VERSION))
|
|
return(-3);
|
|
if(kbver > (unsigned)atoi(LAST_VERSION))
|
|
return(-4);
|
|
|
|
return 0; // file appears to be valid
|
|
}
|
|
|
|
// Reload a keyboard from a file
|
|
int kmfl_reload_keyboard(int keyboard_number)
|
|
{
|
|
KMSI *p;
|
|
XKEYBOARD *p_kbd;
|
|
XKEYBOARD *p_newkbd;
|
|
|
|
p_kbd =p_installed_kbd[keyboard_number];
|
|
|
|
if (p_kbd == NULL)
|
|
return -1;
|
|
|
|
// Detach any instances of this keyboard
|
|
for(p=p_first_instance; p; p=p->next)
|
|
{
|
|
if(p->keyboard_number == keyboard_number)
|
|
kmfl_detach_keyboard(p);
|
|
}
|
|
|
|
p_newkbd=kmfl_load_keyboard_from_file(keyboard_filename[keyboard_number]);
|
|
|
|
if (p_newkbd == NULL)
|
|
return -1;
|
|
|
|
p_installed_kbd[keyboard_number]=p_newkbd;
|
|
|
|
free(p_kbd);
|
|
|
|
// reattach this keyboard to instances using this keyboard
|
|
for(p=p_first_instance; p; p=p->next)
|
|
{
|
|
if(p->keyboard_number == keyboard_number)
|
|
kmfl_attach_keyboard(p, keyboard_number);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Reload all keyboards
|
|
int kmfl_reload_all_keyboards(void)
|
|
{
|
|
int n;
|
|
|
|
for(n=0; n < MAX_KEYBOARDS; n++)
|
|
{
|
|
if(p_installed_kbd[n] != NULL)
|
|
kmfl_reload_keyboard(n);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Unload a keyboard that has been installed
|
|
int kmfl_unload_keyboard(int keyboard_number)
|
|
{
|
|
KMSI *p;
|
|
XKEYBOARD *p_kbd=p_installed_kbd[keyboard_number];
|
|
|
|
if (p_kbd == NULL)
|
|
return -1;
|
|
|
|
// Enumerate instances and ensure that no instances are using this keyboard
|
|
for(p=p_first_instance; p; p=p->next)
|
|
{
|
|
if(p->keyboard_number == keyboard_number) return 1;
|
|
}
|
|
|
|
|
|
// Remove keyboard from list and free memory
|
|
DBGMSG(1,"Keyboard %s unloaded\n",p_kbd->name);
|
|
free(keyboard_filename[keyboard_number]);
|
|
free(p_kbd);
|
|
|
|
p_installed_kbd[keyboard_number]=NULL;
|
|
|
|
n_keyboards--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Unload all keyboards from memory
|
|
int kmfl_unload_all_keyboards(void)
|
|
{
|
|
int n;
|
|
|
|
for(n=0; n < MAX_KEYBOARDS; n++)
|
|
{
|
|
if(p_installed_kbd[n] != NULL)
|
|
kmfl_unload_keyboard(n);
|
|
}
|
|
n_keyboards = 0;
|
|
return 0;
|
|
}
|
|
|
|
// Get the number of an installed keyboard from the name
|
|
int kmfl_keyboard_number(char *name)
|
|
{
|
|
unsigned int n;
|
|
|
|
for(n=0; n<MAX_KEYBOARDS; n++)
|
|
{
|
|
if(p_installed_kbd[n] != NULL &&
|
|
strcmp(p_installed_kbd[n]->name,name) == 0)
|
|
return n;
|
|
}
|
|
return UNDEFINED;
|
|
}
|
|
|
|
// Get the name of an installed keyboard from the number
|
|
const char *kmfl_keyboard_name(int keyboard_number)
|
|
{
|
|
if(p_installed_kbd[keyboard_number] != NULL)
|
|
return p_installed_kbd[keyboard_number]->name;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
const char *kmfl_icon_file(int keyboard_number)
|
|
{
|
|
XKEYBOARD *p_kbd;
|
|
XSTORE *stores;
|
|
XGROUP *groups, *gp;
|
|
XRULE *rules;
|
|
ITEM * strings;
|
|
UTF32 *p32;
|
|
UTF8 *p8;
|
|
unsigned int n, nrules;
|
|
static char icon_name[256];
|
|
|
|
*icon_name = 0;
|
|
|
|
if((p_kbd=p_installed_kbd[keyboard_number]) != NULL)
|
|
{
|
|
stores = (XSTORE *)(p_kbd+1);
|
|
groups = (XGROUP *)(stores+p_kbd->nstores);
|
|
rules = (XRULE *)(groups+p_kbd->ngroups);
|
|
|
|
for(n=nrules=0,gp=groups; n<p_kbd->ngroups; n++, gp++)
|
|
{
|
|
nrules += gp->nrules;
|
|
}
|
|
|
|
strings = (ITEM *)(rules+nrules);
|
|
|
|
if(stores[SS_BITMAP].len >= 0)
|
|
{
|
|
p32 = strings + stores[SS_BITMAP].items;
|
|
p8 = (UTF8 *)icon_name;
|
|
IConvertUTF32toUTF8((const UTF32**)&p32,p32+stores[SS_BITMAP].len,&p8,p8+255);
|
|
*p8 = 0;
|
|
}
|
|
}
|
|
return icon_name;
|
|
}
|