feat(linux): Add dbus system service#8509
Conversation
User Test ResultsTest specification and instructions
Test Artifacts |
5f8baaf to
9f3db08
Compare
9f3db08 to
166dc83
Compare
166dc83 to
f27038b
Compare
f27038b to
cf96012
Compare
|
I hope to review this in next few days |
| sd_bus* bus = NULL; | ||
| int result; | ||
|
|
||
| if (capsLock > 1) { |
There was a problem hiding this comment.
Since capsLock is guint32, does that mean any non-zero value is invalid?
There was a problem hiding this comment.
Any value besides 0 and 1 is invalid.
|
|
||
| if (ret < 0) { | ||
| g_error("Failed to connect to system bus: %s", strerror(-ret)); | ||
| return; |
There was a problem hiding this comment.
Does this need exit_loop(slot, bus);?
There was a problem hiding this comment.
Not really, since opening the user bus failed bus is not set, nor slot, but for consistency it would be better to add it.
There was a problem hiding this comment.
This is because C++ RAII means we don't get nice things (try/finally) -- otherwise we need to wrap these variables in a class for automatic destruction, which is tedious boilerplate. Another pattern I see used is goto loop_cleanup, which at least can keep the cleanup inside the same function, and is thus a little clearer, a little shorter, and avoids accidentally missing the return statement, or calling the cleanup twice (which in the current model would be bad because it would result in double-frees):
if (ret < 0) {
g_error("Failed to connect to system bus: %s", strerror(-ret));
goto cleanup;
}
// ... function continues ...
cleanup:
if (bus) sd_bus_release_name(bus, KEYMAN_TESTSVC_BUS_NAME);
if (slot) sd_bus_slot_unref(slot);
if (bus) sd_bus_close_unref(bus);
}
There was a problem hiding this comment.
I don't like gotos, so I put the whole thing in a class to benefit from C++ RAII 😄
There was a problem hiding this comment.
I don't like gotos, so I put the whole thing in a class to benefit from C++ RAII 😄
I thought there was a good chance that you would say that! 🤣
| export WAYLAND_DISPLAY | ||
| WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) |
There was a problem hiding this comment.
I notice this is how the previous code was. But why wouldn't the two lines be combined?
| export WAYLAND_DISPLAY | |
| WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) | |
| export WAYLAND_DISPLAY=$(grep "Using Wayland display" "$TMPFILE" | cut -d"'" -f2) |
There was a problem hiding this comment.
Combining it masks failures. See https://www.shellcheck.net/wiki/SC2155.
Follow-up of code review comments. # Keyman Conventional Commit suggestions: # - Consider appending the text ". Fixes #4273" to your commit message.
darcywong00
left a comment
There was a problem hiding this comment.
lgtm - I think @mcdurdin wants to get to this too.
|
GROUP_LUNAR_WAYLAND |
|
@ermshiperete note the format change for your test skip (the format you used skipped all tests) |
mcdurdin
left a comment
There was a problem hiding this comment.
This looks really solid. I have one request around resource cleanup patterns, because I think the separate-function pattern is a bit fragile. But I didn't really see any other significant issues. Good job!
|
|
||
| if (ret < 0) { | ||
| g_error("Failed to connect to system bus: %s", strerror(-ret)); | ||
| return; |
There was a problem hiding this comment.
This is because C++ RAII means we don't get nice things (try/finally) -- otherwise we need to wrap these variables in a class for automatic destruction, which is tedious boilerplate. Another pattern I see used is goto loop_cleanup, which at least can keep the cleanup inside the same function, and is thus a little clearer, a little shorter, and avoids accidentally missing the return statement, or calling the cleanup twice (which in the current model would be bad because it would result in double-frees):
if (ret < 0) {
g_error("Failed to connect to system bus: %s", strerror(-ret));
goto cleanup;
}
// ... function continues ...
cleanup:
if (bus) sd_bus_release_name(bus, KEYMAN_TESTSVC_BUS_NAME);
if (slot) sd_bus_slot_unref(slot);
if (bus) sd_bus_close_unref(bus);
}
| if (ret > 0) // we processed a request, try to process another one, right-away | ||
| continue; |
There was a problem hiding this comment.
| if (ret > 0) // we processed a request, try to process another one, right-away | |
| continue; | |
| if (ret > 0) { | |
| // we processed a request, try to process another one, right-away | |
| continue; | |
| } |
| if (exitFlag) { | ||
| g_debug("Exiting loop"); | ||
| exit_loop(slot, bus); | ||
| return; | ||
| } |
There was a problem hiding this comment.
| if (exitFlag) { | |
| g_debug("Exiting loop"); | |
| exit_loop(slot, bus); | |
| return; | |
| } | |
| if (exitFlag) { | |
| // `exitFlag` can be modified by the callback function | |
| // `on_exit_method` which can be called by `sd_bus_process` | |
| g_debug("Exiting loop"); | |
| exit_loop(slot, bus); | |
| return; | |
| } |
| static void | ||
| finish(sd_bus_error* error, sd_bus_message* msg, sd_bus * bus) { | ||
| if (error) { sd_bus_error_free(error); } | ||
| if (msg) { sd_bus_message_unref(msg); } | ||
| if (bus) { sd_bus_unref(bus); } | ||
| } |
There was a problem hiding this comment.
I think I'd prefer to see the goto cleanup pattern which I've noted in another comment -- it's less vulnerable to copypasta errors.
| else | ||
| echo "Can't find neither ${COMMON_ARCH_DIR}/release nor ${COMMON_ARCH_DIR}/debug" | ||
| exit 2 | ||
| echo "Can't find neither ../../../build/$(arch)/debug nor ../../../build/$(arch)/release" |
There was a problem hiding this comment.
| echo "Can't find neither ../../../build/$(arch)/debug nor ../../../build/$(arch)/release" | |
| echo "Cannot find ../../../build/$(arch)/debug or ../../../build/$(arch)/release" |
Yay English grammar (or ... "Can find neither a nor b" is technically okay but weird)
| int32_t ret; | ||
| uint32_t state; | ||
|
|
||
| ret_error = NULL; |
There was a problem hiding this comment.
This is a no-op. Did you mean to set the value rather than the pointer?
| ret_error = NULL; | |
| *ret_error = NULL; |
| ) { | ||
| uint32_t state; | ||
|
|
||
| ret_error = NULL; |
There was a problem hiding this comment.
Did you mean to do this?
| ret_error = NULL; | |
| *ret_error = NULL; |
| static int finish(int ret, void *kbd_devices, sd_bus_slot *slot, sd_bus *bus) { | ||
| close_kbd_devices(kbd_devices); | ||
| sd_bus_slot_unref(slot); | ||
| sd_bus_unref(bus); | ||
|
|
||
| return ret < 0 ? 1 : 0; | ||
| } |
There was a problem hiding this comment.
Again, would prefer a goto cleanup pattern
There was a problem hiding this comment.
Refactored to make use of C++ RAII.
| if (ret > 0) // we processed a request, try to process another one, right-away | ||
| continue; |
There was a problem hiding this comment.
| if (ret > 0) // we processed a request, try to process another one, right-away | |
| continue; | |
| if (ret > 0) { | |
| // we processed a request, try to process another one, right-away | |
| continue; | |
| } |
Also apply changes from code review comments.
| KeymanSystemServiceClient::~KeymanSystemServiceClient() { | ||
| if (error) { sd_bus_error_free(error); } | ||
| if (msg) { sd_bus_message_unref(msg); } | ||
| if (bus) { sd_bus_unref(bus); } | ||
| } |
There was a problem hiding this comment.
In the pattern you have now, if you call SetCapsLockIndicator twice and both times an error is returned, you'll leak error. It would be good to make note of this -- that the class functions are intended to be called just once in the class lifecycle, for future maintainers.
| dbus = g_test_dbus_new(G_TEST_DBUS_NONE); | ||
|
|
||
| // Add the private directory with our in-tree service files. | ||
| g_test_dbus_add_service_dir(dbus, KEYMAN_TEST_SERVICE_PATH); | ||
|
|
||
| std::cout << "Add service dir to " << KEYMAN_TEST_SERVICE_PATH << std::endl; | ||
|
|
||
| // Start the private D-Bus daemon | ||
| g_test_dbus_up(dbus); |
There was a problem hiding this comment.
What happens if any of these functions fail?
There was a problem hiding this comment.
Dunno. Most of these functions are void. g_test_dbus_get_bus_address will return NULL only if g_test_dbus_up hasn't been called. Other than that there are no other possible failures listed in the documentation for these methods.
|
Changes in this pull request will be available for download in Keyman version 17.0.111-alpha |
This implements a dbus system service that allows to provide the missing functionality when running under Wayland.
Fixes #4273.
User Testing
Preparations
The tests should be run on these Linux platforms:
Install build artifacts of this PR
Reboot
Install Nanum fonts for typing Korean:
Change "Complex text layout" setting in LibreOffice Writer:
Expand to see the steps
In LO Tools/Options, go to the Language Settings/Languages tab. Under "Default Languages for Documents" check the box for "Asian" and select "Korean (RoK)" as language, and check "Complex text layout" and select "Khmer" as language.
You'll also have to change the default style: go to Styles/Manage Styles, right-click on "Default Paragraph Style" and select Modify. Go to the Font tab. For Asian Text Font, select Family "NanumMyeongjo" and Language "Korean (RoK)". For CTL Font, select Family "Khmer Mondulkiri" (or another "Khmer" font) and Language "Khmer".
Tests
n>. Verify that the result is "ŋ".n>. Verify that the result is "ŋ".han<space>geul<space>. Verify that the result is "한글".han<space>geul<space>. Verify that the result is "한글".xEjmr. Verify that the output is "ខ្មែរ".xEjmr. Verify that the output is "ខ្មែរ".n>