mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 17:36:00 +00:00
487 lines
14 KiB
C
487 lines
14 KiB
C
/* vim:set et sts=4: */
|
|
|
|
/*
|
|
* KMFL Input Method for IBUS (The Input Bus)
|
|
*
|
|
* Copyright (C) 2009-2018 SIL International
|
|
*
|
|
* kmflutil is dual licensed under the MIT or GPL licenses as described below.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* MIT license
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* OR
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <glib.h>
|
|
#include <glib/gprintf.h>
|
|
#include <string.h>
|
|
|
|
#include "kmflutil.h"
|
|
|
|
#define N_(text) text
|
|
static gchar * get_dirname(const gchar * path)
|
|
{
|
|
gchar * dirend = g_strrstr(path, "/");
|
|
|
|
if (dirend) {
|
|
return g_strndup(path, dirend-path);
|
|
} else {
|
|
return g_strdup("");
|
|
}
|
|
}
|
|
|
|
GList * kmfl_get_keyboard_fromdir( GList *keyboard_list, const gchar * path)
|
|
{
|
|
// g_message("KMFL: getting from dir: %s", path);
|
|
DIR *dir = opendir(path);
|
|
|
|
if (dir != NULL) {
|
|
struct dirent *file = readdir(dir);
|
|
while (file != NULL) {
|
|
struct stat filestat;
|
|
gchar * absfn = g_strdup_printf("%s/%s", path, file->d_name);
|
|
stat(absfn, &filestat);
|
|
|
|
if (S_ISDIR(filestat.st_mode))
|
|
{
|
|
if(strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
|
|
keyboard_list = kmfl_get_keyboard_fromdir(keyboard_list, absfn);
|
|
g_free(absfn);
|
|
}
|
|
|
|
// Only .kmfl and .kmn extensions are valid keyboard files
|
|
else if (S_ISREG(filestat.st_mode)
|
|
&& ((g_str_has_suffix(absfn, ".kmfl") && kmfl_check_keyboard(absfn) == 0)
|
|
|| g_str_has_suffix(absfn, ".kmn"))) {
|
|
|
|
keyboard_list=g_list_append(keyboard_list, absfn);
|
|
}
|
|
else
|
|
{
|
|
g_free(absfn);
|
|
}
|
|
|
|
file = readdir(dir);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
return keyboard_list;
|
|
}
|
|
|
|
GList * kmfl_get_keyboard_list( const gchar * path)
|
|
{
|
|
GList * keyboard_list=NULL;
|
|
keyboard_list = kmfl_get_keyboard_fromdir(keyboard_list, path);
|
|
return keyboard_list;
|
|
}
|
|
|
|
gchar * kmfl_get_icon_file(KInputMethod * im)
|
|
{
|
|
const char * icon_file = kmfl_icon_file(im->keyboard_number);
|
|
gchar * full_path_to_icon_file=NULL;
|
|
struct stat filestat;
|
|
char * valid_extensions[]= {"", ".png", ".bmp", ".jpg", NULL};
|
|
int valid_extension_index;
|
|
|
|
if (strlen(icon_file) > 0) {
|
|
for (valid_extension_index=0; valid_extensions[valid_extension_index] != NULL; valid_extension_index++) {
|
|
full_path_to_icon_file=g_strdup_printf("%s/%s%s", get_dirname(im->keyboard_filename), icon_file, valid_extensions[valid_extension_index]);
|
|
|
|
stat(full_path_to_icon_file, &filestat);
|
|
|
|
if (S_ISREG(filestat.st_mode)) {
|
|
break;
|
|
} else {
|
|
g_free(full_path_to_icon_file);
|
|
full_path_to_icon_file=NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (full_path_to_icon_file==NULL)
|
|
full_path_to_icon_file=g_strdup("/usr/share/kmfl/icons/default.png");
|
|
|
|
return full_path_to_icon_file;
|
|
}
|
|
|
|
gchar * kmfl_get_ldml_file(KInputMethod * im)
|
|
{
|
|
gchar * full_path_to_ldml_file=NULL, *p, *filename;
|
|
struct stat filestat;
|
|
|
|
if(g_strcmp0(im->keyboard_visualkeyboard, "") == 0)
|
|
{
|
|
g_debug("WDG: no kvk(s) so no ldml file");
|
|
return g_strdup("");
|
|
}
|
|
p=rindex(im->keyboard_visualkeyboard,'.');
|
|
if (p==NULL)
|
|
{
|
|
g_debug("WDG: couldn't find . in vk filename %s", im->keyboard_visualkeyboard);
|
|
return g_strdup("");
|
|
}
|
|
if (strncmp(p, ".kvk", 4) != 0) // sometimes kmn have kvks as the "visual keyboard"
|
|
{
|
|
g_debug("WDG: visual keyboard is not a kvk. Filename: %s", im->keyboard_visualkeyboard);
|
|
return g_strdup("");
|
|
}
|
|
filename = g_strndup(im->keyboard_visualkeyboard, p-(im->keyboard_visualkeyboard));
|
|
full_path_to_ldml_file=g_strdup_printf("%s/%s.ldml", get_dirname(im->keyboard_filename), filename);
|
|
g_free(filename);
|
|
// don't forget to stat for the file before returning
|
|
stat(full_path_to_ldml_file, &filestat);
|
|
|
|
if (!S_ISREG(filestat.st_mode)) {
|
|
g_debug("WDG: couldn't find ldml file %s", full_path_to_ldml_file);
|
|
g_free(full_path_to_ldml_file);
|
|
full_path_to_ldml_file=g_strdup("");
|
|
}
|
|
return full_path_to_ldml_file;
|
|
}
|
|
|
|
void kmfl_get_keyboard_info(KInputMethod * im)
|
|
{
|
|
char buf[1024];
|
|
KMSI * p_kmsi;
|
|
|
|
im->keyboard_name = g_strdup(kmfl_keyboard_name(im->keyboard_number));
|
|
p_kmsi = kmfl_make_keyboard_instance(NULL);
|
|
kmfl_attach_keyboard(p_kmsi, im->keyboard_number);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_AUTHOR,buf,sizeof(buf) - 1);
|
|
im->keyboard_author=g_strdup(buf);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_COPYRIGHT,buf,sizeof(buf) - 1);
|
|
im->keyboard_copyright=g_strdup(buf);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_LANGUAGE,buf,sizeof(buf) - 1);
|
|
im->keyboard_language=g_strdup(buf);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_MESSAGE,buf,sizeof(buf) - 1);
|
|
im->keyboard_description=g_strdup(buf);
|
|
|
|
if (g_strrstr(buf, "license") || g_strrstr(buf, "License") || g_strrstr(buf, "LICENSE"))
|
|
im->keyboard_license=g_strdup(buf);
|
|
else
|
|
im->keyboard_license=g_strdup("");
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_VISUALKEYBOARD,buf,sizeof(buf) - 1);
|
|
im->keyboard_visualkeyboard=g_strdup(buf);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_KEYBOARDVERSION,buf,sizeof(buf) - 1);
|
|
im->keyboard_keyboardversion=g_strdup(buf);
|
|
|
|
*buf='\0';
|
|
kmfl_get_header(p_kmsi,SS_LAYOUT,buf,sizeof(buf) - 1);
|
|
if (*buf != '\0')
|
|
im->keyboard_layout=g_strdup(buf);
|
|
else
|
|
im->keyboard_layout=g_strdup("us");
|
|
|
|
kmfl_detach_keyboard(p_kmsi);
|
|
kmfl_delete_keyboard_instance(p_kmsi);
|
|
im->keyboard_icon_filename = kmfl_get_icon_file(im);
|
|
im->keyboard_ldmlfile = kmfl_get_ldml_file(im);
|
|
}
|
|
|
|
void kmfl_free_keyboard_info(KInputMethod * im)
|
|
{
|
|
g_assert(im != NULL);
|
|
g_free(im->keyboard_filename);
|
|
g_free(im->keyboard_name);
|
|
g_free(im->keyboard_author);
|
|
g_free(im->keyboard_copyright);
|
|
g_free(im->keyboard_language);
|
|
g_free(im->keyboard_description);
|
|
g_free(im->keyboard_icon_filename);
|
|
g_free(im->keyboard_layout);
|
|
g_free(im->keyboard_license);
|
|
g_free(im->keyboard_visualkeyboard);
|
|
g_free(im->keyboard_keyboardversion);
|
|
g_free(im->keyboard_ldmlfile);
|
|
}
|
|
|
|
|
|
static IBusEngineDesc *
|
|
ibus_kmfl_engine_new (gchar * file_name,
|
|
gchar *lang,
|
|
gchar *name,
|
|
gchar *author,
|
|
gchar *icon,
|
|
gchar *copyright,
|
|
gchar *description,
|
|
gchar *layout,
|
|
gchar *license)
|
|
{
|
|
IBusEngineDesc *engine;
|
|
gchar * desc = g_strdup_printf("%s\n%s", description, copyright);
|
|
|
|
engine = ibus_engine_desc_new (file_name,
|
|
name,
|
|
desc ? desc : "",
|
|
lang,
|
|
license ? license : "",
|
|
author ? author : "",
|
|
icon ? icon : "",
|
|
layout);
|
|
|
|
return engine;
|
|
}
|
|
|
|
GList *
|
|
ibus_kmfl_add_engines(GList * engines, GList * keyboard_list)
|
|
{
|
|
GList *p;
|
|
for (p=keyboard_list; p != NULL; p = p->next) {
|
|
gchar * keyboard_filename = (gchar *) p->data;
|
|
KInputMethod im;
|
|
|
|
im.keyboard_number = kmfl_load_keyboard(keyboard_filename);
|
|
if (im.keyboard_number >=0) {
|
|
im.keyboard_filename = g_strdup(keyboard_filename);
|
|
|
|
kmfl_get_keyboard_info(&im);
|
|
engines = g_list_append (engines, ibus_kmfl_engine_new (keyboard_filename, im.keyboard_language, im.keyboard_name, im.keyboard_author, im.keyboard_icon_filename, im.keyboard_copyright, im.keyboard_description, im.keyboard_layout, im.keyboard_license));
|
|
g_free(p->data);
|
|
kmfl_free_keyboard_info(&im);
|
|
}
|
|
}
|
|
return engines;
|
|
}
|
|
|
|
GList *
|
|
ibus_kmfl_list_engines (void)
|
|
{
|
|
GList *engines = NULL;
|
|
GList *keyboard_list;
|
|
gchar *local_keyboard_path;
|
|
|
|
keyboard_list = kmfl_get_keyboard_list("/usr/share/kmfl");
|
|
engines = ibus_kmfl_add_engines(engines, keyboard_list);
|
|
g_list_free(keyboard_list);
|
|
|
|
keyboard_list = kmfl_get_keyboard_list("/usr/local/share/kmfl");
|
|
engines = ibus_kmfl_add_engines(engines, keyboard_list);
|
|
g_list_free(keyboard_list);
|
|
|
|
local_keyboard_path= g_strdup_printf("%s/.kmfl", getenv("HOME"));
|
|
keyboard_list = kmfl_get_keyboard_list(local_keyboard_path);
|
|
engines = ibus_kmfl_add_engines(engines, keyboard_list);
|
|
g_free(local_keyboard_path);
|
|
g_list_free(keyboard_list);
|
|
|
|
return engines;
|
|
}
|
|
|
|
IBusComponent *
|
|
ibus_kmfl_get_component (void)
|
|
{
|
|
GList *engines, *p;
|
|
IBusComponent *component;
|
|
|
|
component = ibus_component_new ("org.freedesktop.IBus.KMFL",
|
|
N_("KMFL"),
|
|
"0.1.0",
|
|
"GPL",
|
|
"Doug Rintoul <doug_rintoul@sil.org",
|
|
"http://kmfl.sourceforge.org",
|
|
"",
|
|
"ibus-kmfl");
|
|
|
|
engines = ibus_kmfl_list_engines ();
|
|
|
|
for (p = engines; p != NULL; p = p->next) {
|
|
ibus_component_add_engine (component, (IBusEngineDesc *) p->data);
|
|
}
|
|
|
|
g_list_free (engines);
|
|
return component;
|
|
}
|
|
|
|
KInputMethod * kinput_open_im(const gchar * keyboard_filename)
|
|
{
|
|
KInputMethod * im = g_new(KInputMethod, 1);
|
|
|
|
if (im != NULL) {
|
|
im->keyboard_number = kmfl_load_keyboard(keyboard_filename);
|
|
if (im->keyboard_number < 0) {
|
|
g_free(im);
|
|
im = NULL;
|
|
} else {
|
|
im->keyboard_filename = g_strdup(keyboard_filename);
|
|
kmfl_get_keyboard_info(im) ;
|
|
}
|
|
}
|
|
return im;
|
|
}
|
|
|
|
void kinput_close_im(KInputMethod * im)
|
|
{
|
|
g_assert(im != NULL);
|
|
kmfl_free_keyboard_info(im);
|
|
kmfl_unload_keyboard(im->keyboard_number);
|
|
g_free(im);
|
|
}
|
|
|
|
void output_string(void *contrack, char *ptr) {
|
|
KInputContext * ic = (KInputContext *)contrack;
|
|
g_debug("DAR: output_string - ic->cmds=%p", ic->cmds);
|
|
if (ptr) {
|
|
Cmd * cmd = g_new(Cmd, 1);
|
|
cmd->opcode=OUTPUT_STRING;
|
|
cmd->cmdarg=g_strdup(ptr);
|
|
ic->cmds = g_list_append(ic->cmds, cmd);
|
|
}
|
|
}
|
|
|
|
void erase_char(void *contrack) {
|
|
KInputContext * ic = (KInputContext *)contrack;
|
|
g_debug("DAR: erase_char - ic->cmds=%p", ic->cmds);
|
|
Cmd * cmd = g_new(Cmd, 1);
|
|
cmd->opcode = ERASE_CHAR;
|
|
cmd->cmdarg=NULL;
|
|
ic->cmds = g_list_append(ic->cmds, cmd);
|
|
}
|
|
|
|
void output_char(void *contrack, unsigned char byte) {
|
|
if (byte == 8) {
|
|
erase_char(contrack);
|
|
} else {
|
|
char s[2];
|
|
s[0] = byte;
|
|
s[1] = '\0';
|
|
output_string(contrack, s);
|
|
}
|
|
}
|
|
|
|
void forward_keyevent(void *contrack, unsigned int key, unsigned int state)
|
|
{
|
|
|
|
}
|
|
|
|
void output_beep(void *contrack) {
|
|
KInputContext * ic = (KInputContext *)contrack;
|
|
Cmd * cmd = g_new(Cmd, 1);
|
|
cmd->opcode = OUTPUT_BEEP;
|
|
cmd->cmdarg=NULL;
|
|
ic->cmds = g_list_append(ic->cmds, cmd);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create an input context
|
|
*/
|
|
KInputContext *
|
|
kmfl_create_ic(KInputMethod *im)
|
|
{
|
|
KInputContext * ic;
|
|
g_debug("DAR: kmfl_create_ic");
|
|
ic = g_new(KInputContext, 1);
|
|
ic->cmds = NULL;
|
|
ic->p_kmsi=kmfl_make_keyboard_instance(ic);
|
|
kmfl_attach_keyboard(ic->p_kmsi, im->keyboard_number);
|
|
|
|
return ic;
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy an input context
|
|
*/
|
|
void
|
|
kmfl_destroy_ic(KInputContext *ic)
|
|
{
|
|
GList * p;
|
|
g_debug("DAR: kmfl_destroy_ic");
|
|
kmfl_detach_keyboard(ic->p_kmsi);
|
|
kmfl_delete_keyboard_instance(ic->p_kmsi);
|
|
|
|
if (ic->cmds) {
|
|
for (p=ic->cmds; p != NULL; p = p->next) {
|
|
Cmd * cmd = (Cmd *)p->data;
|
|
if (cmd) {
|
|
if (cmd->cmdarg)
|
|
g_free(cmd->cmdarg);
|
|
g_free(cmd);
|
|
}
|
|
}
|
|
g_list_free(ic->cmds);
|
|
ic->cmds=NULL;
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
#include <locale.h>
|
|
|
|
int main ()
|
|
{
|
|
IBusComponent *component;
|
|
GString *output;
|
|
|
|
setlocale (LC_ALL, "");
|
|
ibus_init ();
|
|
|
|
component = ibus_kmfl_get_component ();
|
|
|
|
output = g_string_new ("");
|
|
|
|
ibus_component_output (component, output, 1);
|
|
|
|
g_debug ("\n%s", output->str);
|
|
|
|
g_string_free (output, TRUE);
|
|
g_object_unref (component);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|