From a6732ad38eaa887dca17c24de79225ef0de136da Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Thu, 22 Jul 2021 13:47:13 +0700 Subject: [PATCH 1/7] refactor: change to normal linking instead of runtime linking --- .gitmodules | 3 - lib/libkbbi | 1 - src/lib.c | 157 ---------------------------------------------------- src/lib.h | 78 -------------------------- src/main.c | 78 +++++++++++++------------- 5 files changed, 38 insertions(+), 279 deletions(-) delete mode 100644 .gitmodules delete mode 160000 lib/libkbbi delete mode 100644 src/lib.c delete mode 100644 src/lib.h diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index fbf41c6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/libkbbi"] - path = lib/libkbbi - url = git@github.com:misterabdul/libkbbi.git diff --git a/lib/libkbbi b/lib/libkbbi deleted file mode 160000 index 0c1af35..0000000 --- a/lib/libkbbi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0c1af351af6c68d4f267a6efb9c2b388c6ab5841 diff --git a/src/lib.c b/src/lib.c deleted file mode 100644 index 59f2efb..0000000 --- a/src/lib.c +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include -#include - -#include "lib.h" - -/** - * The Lib's private data struct. - */ -typedef struct _private -{ - void* soHandler; - Results (*initResult)(void); - void (*freeResult)(Results); - int (*search)(Results*, int*, char*, int); - int (*count)(void); - char* (*version)(void); -} * Private; - -int -Lib_load(Lib* lib, const char* path) -{ - void* soHandler = dlopen(path, RTLD_LAZY); - if (!soHandler) - return 0; - - /* clear any existing eror */ - dlerror(); - - Results (*initResult)(void); - void (*freeResult)(Results); - int (*search)(Results*, int*, char*, int); - int (*count)(void); - char* (*version)(void); - - *(void**)(&initResult) = dlsym(soHandler, "init_result"); - *(void**)(&freeResult) = dlsym(soHandler, "free_result"); - *(void**)(&search) = dlsym(soHandler, "search"); - *(void**)(&count) = dlsym(soHandler, "count"); - *(void**)(&version) = dlsym(soHandler, "version"); - - if (dlerror()) - return 0; - - Private libPrivateInstance = malloc(sizeof(struct _private)); - libPrivateInstance->soHandler = soHandler; - libPrivateInstance->initResult = initResult; - libPrivateInstance->freeResult = freeResult; - libPrivateInstance->search = search; - libPrivateInstance->count = count; - libPrivateInstance->version = version; - - Lib libInstance = malloc(sizeof(struct _lib)); - libInstance->results = NULL; - libInstance->resultSize = 0; - libInstance->private = libPrivateInstance; - *lib = libInstance; - - return 1; -} - -/** - * Custom function to free the result recursively. - * - * @param[in] result - */ -void -freeResult(Results result) -{ - if (result) { - // if (result->artikata) - // free(result->artikata); - // if (result->katakunci) - // free(result->katakunci); - freeResult(result->next); - free(result); - } -} - -void -Lib_freeResult(Lib* libInstance) -{ - if (*libInstance) - freeResult((*libInstance)->results); - - (*libInstance)->results = NULL; - (*libInstance)->resultSize = 0; -} - -void -Lib_close(Lib* libInstance) -{ - if (*libInstance) { - Private libPrivateInstance = (Private)(*libInstance)->private; - - if (libPrivateInstance && libPrivateInstance->soHandler) { - dlclose(libPrivateInstance->soHandler); - free(libPrivateInstance); - } - } - - free(*libInstance); - *libInstance = NULL; -} - -int -Lib_search(Lib libInstance, const char* query) -{ - int status = 0; - - if (libInstance && query) { - Private libPrivateInstance = (Private)libInstance->private; - - if (libPrivateInstance && libPrivateInstance->search && - libPrivateInstance->initResult && libPrivateInstance->freeResult) { - Results results = libPrivateInstance->initResult(); - int resultSize = 0; - status = - libPrivateInstance->search(&results, &resultSize, query, strlen(query)); - - if (libInstance->results) - libPrivateInstance->freeResult(libInstance->results); - libInstance->results = results; - libInstance->resultSize = resultSize; - } - } - - return status; -} - -int -Lib_count(Lib libInstance) -{ - if (libInstance) { - Private libPrivateInstance = (Private)libInstance->private; - - if (libPrivateInstance && libPrivateInstance->count) { - return libPrivateInstance->count(); - } - } - - return 0; -} - -char* -Lib_version(Lib libInstance) -{ - if (libInstance) { - Private libPrivateInstance = (Private)libInstance->private; - - if (libPrivateInstance && libPrivateInstance->version) { - return libPrivateInstance->version(); - } - } - - return NULL; -} diff --git a/src/lib.h b/src/lib.h deleted file mode 100644 index a65a5e2..0000000 --- a/src/lib.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _LIB_H -#define _LIB_H - -/** - * The results struct. - */ -typedef struct _results -{ - char* katakunci; - char* artikata; - struct _results* next; -} * Results; - -/** - * The lib struct. - */ -typedef struct _lib -{ - Results results; - int resultSize; - void* private; -} * Lib; - -/** - * Load the lib from given path. - * - * @param[out] lib The lib instance. - * @param[in] path The lib's path. - * @return Result of the load lib process. - */ -int -Lib_load(Lib* lib, const char* path); - -/** - * Close the lib. - * - * @param[out] lib The lib instance. - */ -void -Lib_close(Lib* lib); - -/** - * Free the result instance. - * - * @param[out] lib The lib instance. - */ -void -Lib_freeResult(Lib* lib); - -/** - * Do search on lib with given query. - * - * @param[in] lib The lib instance. - * @param[in] query The search query. - * @return Status of the search (found or not found). - */ -int -Lib_search(Lib lib, const char* query); - -/** - * Count all words inside the lib. - * - * @param[in] lib The lib instance. - * @return Total number of words inside the lib. - */ -int -Lib_count(Lib lib); - -/** - * Get the current lib version. - * - * @param[in] lib The lib instance. - * @return The current lib version string. - */ -char* -Lib_version(Lib lib); - -#endif diff --git a/src/main.c b/src/main.c index 16c3843..937e751 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,8 @@ #include +#include + +#include -#include "lib.h" #include "ui.h" #define APP_ID "io.github.misterabdul.kbbi-gtk" @@ -8,12 +10,7 @@ #define WINDOW_WIDTH 500 #define WINDOW_HEIGHT 400 -#define LIB_KBBI_SO_PATH "./libkbbi.so" - -/** - * The lib instance. - */ -Lib lib = NULL; +KBBI_Results results = NULL; /** * Result words array. @@ -45,39 +42,43 @@ onDialogResponded(UI ui) void onSearchButtonClicked(UI ui, char* query) { - UI_setWebViewContent(ui, "
"); + if (strlen(query) > 0) { + UI_setWebViewContent(ui, "
"); - int found = Lib_search(lib, query); + if (!results) + results = KBBI_resultInit(); - if (found && lib->results) { - char** words = malloc(lib->resultSize * sizeof(char*)); - char** means = malloc(lib->resultSize * sizeof(char*)); + int resultSize = 0; + int found = KBBI_search(&results, &resultSize, query, strlen(query)); - Results results = lib->results; - for (int i = 0; i < lib->resultSize; i++) { - if (!results) - break; + if (found && results) { + char** words = malloc(resultSize * sizeof(char*)); + char** means = malloc(resultSize * sizeof(char*)); - if (results->katakunci) - words[i] = results->katakunci; + KBBI_Results resultNode = results; + for (int i = 0; i < resultSize; i++) { + if (!resultNode) + break; - if (results->artikata) - means[i] = results->artikata; + if (resultNode->katakunci) + words[i] = resultNode->katakunci; - results = results->next; - } + if (resultNode->artikata) + means[i] = resultNode->artikata; - UI_setListViewItems(ui, words, lib->resultSize); + resultNode = resultNode->next; + } - if (resultWords) - free(resultWords); - resultWords = words; + UI_setListViewItems(ui, words, resultSize); - if (resultMeans) - free(resultMeans); - resultMeans = means; + if (resultWords) + free(resultWords); + resultWords = words; - Lib_freeResult(&lib); + if (resultMeans) + free(resultMeans); + resultMeans = means; + } } } @@ -102,13 +103,6 @@ onListViewItemClicked(UI ui, char* word, int index) void onAppRunning(UI ui) { - int isLoaded = Lib_load(&lib, LIB_KBBI_SO_PATH); - if (!isLoaded) - UI_showDialog(ui, - "Kesalahan", - "Tidak dapat memuat file \"libkbbi.so\"", - onDialogResponded); - UI_onListViewItemClicked(ui, onListViewItemClicked); UI_onSearchButtonClicked(ui, onSearchButtonClicked); } @@ -127,11 +121,15 @@ main(int argc, char** argv) int status = UI_run(ui, argc, argv, onAppRunning); - if (lib) - Lib_close(&lib); - if (ui) UI_destroy(&ui); + if (results) + KBBI_resultFree(results); + if (resultWords) + free(resultWords); + if (resultMeans) + free(resultMeans); + return status; } From a5d106837bec9d6cec86afb91c2edb96bbe2986a Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Thu, 22 Jul 2021 13:49:02 +0700 Subject: [PATCH 2/7] build: prepare to migrate to cmake --- bin/.gitignore | 2 -- makefile | 42 ------------------------------------------ obj/.gitignore | 2 -- 3 files changed, 46 deletions(-) delete mode 100644 bin/.gitignore delete mode 100644 makefile delete mode 100644 obj/.gitignore diff --git a/bin/.gitignore b/bin/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/bin/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/makefile b/makefile deleted file mode 100644 index 52b9e6c..0000000 --- a/makefile +++ /dev/null @@ -1,42 +0,0 @@ -BIN := kbbi-gtk - -SRCDIR := src -OBJDIR := obj -BINDIR := bin -LIBDIR := lib - -LIB_KBBI := libkbbi - -PKG_CFG_GTK4 := $(shell pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0) -ldl - -CFLAGS ?= -Wall -Wextra -g - -BIN_SRCS := $(shell find $(SRCDIR) -name *.c) -BIN_OBJS := $(subst $(SRCDIR)/,$(SRCDIR)_,$(BIN_SRCS:%=$(OBJDIR)/%.o)) - -.PHONY: all - -all: $(BINDIR)/$(BIN) $(BINDIR)/$(LIB_KBBI).so - -clean: - @$(RM) -rf bin/* - @$(RM) -rf obj/* - -# Link all the objects to create main binary -$(BINDIR)/$(BIN): $(BIN_OBJS) - @echo LINK $(BIN) - @$(CC) $(BIN_OBJS) -o $@ $(PKG_CFG_GTK4) $(CFLAGS) - -# Compile each main source code -$(OBJDIR)/$(SRCDIR)_%.c.o: $(SRCDIR)/%.c - @echo CC $< - @$(CC) -c $< -o $@ $(PKG_CFG_GTK4) $(CFLAGS) - -# Move libkbbi.so -$(BINDIR)/$(LIB_KBBI).so: $(LIBDIR)/$(LIB_KBBI)/bin/$(LIB_KBBI).so - @mv $(LIBDIR)/$(LIB_KBBI)/bin/$(LIB_KBBI).so $(BINDIR)/ - -# Compile libkbbi.so -$(LIBDIR)/$(LIB_KBBI)/bin/$(LIB_KBBI).so: - @cd $(LIBDIR)/$(LIB_KBBI); \ - $(MAKE) all diff --git a/obj/.gitignore b/obj/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/obj/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore From f1b60bd3ad89dd76a8723879aa37422d7ad88228 Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Thu, 22 Jul 2021 14:46:54 +0700 Subject: [PATCH 3/7] build: migrate build system to cmake --- .gitignore | 1 + CMakeLists.txt | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..89fce96 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.9) + +# Project description +project( + kbbi-gtk + VERSION 1.0.0 + DESCRIPTION "KBBI Offline with GTK3" +) + +# Check required package & libraries +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK3 REQUIRED gtk+-3.0) +pkg_check_modules(WEBKIT2GTK4 REQUIRED webkit2gtk-4.0) +pkg_check_modules(LIBKBBI REQUIRED libkbbi) + +# Set C standard +set(CMAKE_C_STANDARD 99) + +# Targets +add_executable( + ${PROJECT_NAME} + src/main.c + src/ui.c +) + +# Properties +set_target_properties( + ${PROJECT_NAME} PROPERTIES + VERSION ${PROJECT_VERSION} +) + +# Includes +target_include_directories( + ${PROJECT_NAME} + PRIVATE src +) + +# Link required libraries +target_link_libraries( + ${PROJECT_NAME} + ${GTK3_LIBRARIES} + ${WEBKIT2GTK4_LIBRARIES} + ${LIBKBBI_LIBRARIES} +) + +# Include library's public header directory +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${GTK_INCLUDE_DIRS} + PUBLIC ${WEBKIT2GTK4_INCLUDE_DIRS} + PUBLIC ${LIBKBBI_INCLUDE_DIRS} +) + +# Add library's extra compiler flags +target_compile_options( + ${PROJECT_NAME} + PUBLIC ${GTK3_CLFAGS_OTHER} + PUBLIC ${WEBKIT2GTK4_CLFAGS_OTHER} + PUBLIC ${LIBKBBI_CLFAGS_OTHER} +) + +# CMake setup +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() +set(CMAKE_C_FLAGS "-Wall -Wextra -fvisibility=hidden") +set(CMAKE_C_FLAGS_DEBUG "-g") +set(CMAKE_C_FLAGS_RELEASE "-O3") From d46864abd137af252115824668e712844c9f4c4d Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Thu, 22 Jul 2021 14:51:31 +0700 Subject: [PATCH 4/7] docs(readme): update readme --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index d927c8c..60a6a26 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,22 @@ KBBI offline remake with GTK3. This is my hobby project to learn more about C programming, widget programming using GTK, and dynamic library. Feel free to explore, fork, or create a pull request if you interested. ![Alt text](./assets/screenshots/screenshot_1.png "KBBI Offline") + +## Todo + +- [ ] Unit testing with CMake (?) + +## Building + +```sh +# Create build directory +$ mkdir -p build +$ cd build + +# Run cmake to generate build files +$ cmake .. + +# Run make to start building +$ make + +``` From 2ac1abc3aac7ace4b3abc70b07ae1f0c13836196 Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Thu, 22 Jul 2021 15:17:31 +0700 Subject: [PATCH 5/7] build: add install & uninstall cmake target --- CMakeLists.txt | 19 +++++++++++++++++++ uninstall.cmake.in | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 uninstall.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 89fce96..1c912a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,3 +66,22 @@ endif() set(CMAKE_C_FLAGS "-Wall -Wextra -fvisibility=hidden") set(CMAKE_C_FLAGS_DEBUG "-g") set(CMAKE_C_FLAGS_RELEASE "-O3") + +# Install the file +install( + TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +# Expand uninstall script +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake" + IMMEDIATE @ONLY +) + +# Add uninstall target +add_custom_target( + uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake +) diff --git a/uninstall.cmake.in b/uninstall.cmake.in new file mode 100644 index 0000000..c2d34d4 --- /dev/null +++ b/uninstall.cmake.in @@ -0,0 +1,21 @@ +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() From ec35eac3d9ef4594485426e531ff01f68977b3eb Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Fri, 23 Jul 2021 09:25:14 +0700 Subject: [PATCH 6/7] build: properly install as linux desktop application --- CMakeLists.txt | 25 +++++++++++++++++++++++++ README.md | 6 ++++++ application.desktop.in | 7 +++++++ assets/icons/icon.png | Bin 0 -> 4586 bytes 4 files changed, 38 insertions(+) create mode 100644 application.desktop.in create mode 100644 assets/icons/icon.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c912a2..01ef59b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,11 +67,36 @@ set(CMAKE_C_FLAGS "-Wall -Wextra -fvisibility=hidden") set(CMAKE_C_FLAGS_DEBUG "-g") set(CMAKE_C_FLAGS_RELEASE "-O3") +set(DESKTOP_PATH "share/applications/") +set(DESKTOP_ICON_PATH "share/icons/hicolor/128x128/apps") +set(DESKTOP_NAME "KBBI Offline") +set(DESKTOP_EXEC ${PROJECT_NAME}) +set(DESKTOP_ICON_REF "${CMAKE_INSTALL_PREFIX}/${DESKTOP_ICON_PATH}/${PROJECT_NAME}.png") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/application.desktop.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.desktop" + @ONLY +) +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/assets/icons/icon.png" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.png" + COPYONLY +) + # Install the file install( TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.desktop + DESTINATION ${DESKTOP_PATH} +) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.png + DESTINATION ${DESKTOP_ICON_PATH} +) # Expand uninstall script configure_file( diff --git a/README.md b/README.md index 60a6a26..bab3de7 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,10 @@ $ cmake .. # Run make to start building $ make +# Optional: run install script to install the apps +$ sudo make install + +# Optional: run uninstall script to uninstall the apps +$ sudo make uninstall + ``` diff --git a/application.desktop.in b/application.desktop.in new file mode 100644 index 0000000..5835fa0 --- /dev/null +++ b/application.desktop.in @@ -0,0 +1,7 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=@DESKTOP_NAME@ +Exec=@DESKTOP_EXEC@ +Icon=@DESKTOP_ICON_REF@ +Categories=Utility; diff --git a/assets/icons/icon.png b/assets/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f42b5524b2af32d8b626ba61074f5792eb27bfce GIT binary patch literal 4586 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 literal 0 HcmV?d00001 From b85b9a80ae6e610eec26772fc25d85e7abee055e Mon Sep 17 00:00:00 2001 From: Abdul Rahmad Pasaribu Date: Fri, 23 Jul 2021 09:26:25 +0700 Subject: [PATCH 7/7] docs(readme): update todo list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bab3de7..be85e7b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This is my hobby project to learn more about C programming, widget programming u ## Todo +- [ ] AppImage packaging - [ ] Unit testing with CMake (?) ## Building