Initial espanso-info version for Wayland
This commit is contained in:
parent
5df94b5031
commit
3bc4f5a105
|
@ -49,7 +49,20 @@ fn cc_config() {
|
|||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
println!("cargo:rustc-link-lib=dylib=X11");
|
||||
} else {
|
||||
// Nothing to compile on wayland
|
||||
println!("cargo:rerun-if-changed=src/wayland/native.h");
|
||||
println!("cargo:rerun-if-changed=src/wayland/native.c");
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include("src/wayland")
|
||||
.include("/usr/include/dbus-1.0")
|
||||
.include("/usr/lib64/dbus-1.0/include")
|
||||
.file("src/wayland/native.cpp")
|
||||
.compile("espansoinfo");
|
||||
|
||||
println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
|
||||
println!("cargo:rustc-link-lib=static=espansoinfo");
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
println!("cargo:rustc-link-lib=dylib=dbus-1");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
27
espanso-info/src/wayland/ffi.rs
Normal file
27
espanso-info/src/wayland/ffi.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso 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 espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[link(name = "espansoinfo", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn info_get_title(buffer: *mut c_char, buffer_size: i32) -> i32;
|
||||
pub fn info_get_exec(buffer: *mut c_char, buffer_size: i32) -> i32;
|
||||
pub fn info_get_class(buffer: *mut c_char, buffer_size: i32) -> i32;
|
||||
}
|
|
@ -17,23 +17,78 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// This file:
|
||||
// Last changes: 2022-04-05T17:55:36+02:00 by Hendrik G. Seliger (github@hseliger.eu)
|
||||
|
||||
use std::{ffi::CStr, os::raw::c_char};
|
||||
|
||||
use crate::{AppInfo, AppInfoProvider};
|
||||
|
||||
use self::ffi::{info_get_class, info_get_exec, info_get_title};
|
||||
|
||||
mod ffi;
|
||||
|
||||
pub(crate) struct WaylandAppInfoProvider {}
|
||||
|
||||
impl WaylandAppInfoProvider {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppInfoProvider for WaylandAppInfoProvider {
|
||||
// TODO: can we read these info on Wayland?
|
||||
fn get_info(&self) -> AppInfo {
|
||||
AppInfo {
|
||||
title: None,
|
||||
exec: None,
|
||||
class: None,
|
||||
}
|
||||
}
|
||||
fn get_info(&self) -> AppInfo {
|
||||
AppInfo {
|
||||
title: self.get_title(),
|
||||
class: self.get_class(),
|
||||
exec: self.get_exec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandAppInfoProvider {
|
||||
fn get_exec(&self) -> Option<String> {
|
||||
let mut buffer: [c_char; 2048] = [0; 2048];
|
||||
if unsafe { info_get_exec(buffer.as_mut_ptr(), (buffer.len() - 1) as i32) } > 0 {
|
||||
let string = unsafe { CStr::from_ptr(buffer.as_ptr()) };
|
||||
let string = string.to_string_lossy();
|
||||
if !string.is_empty() {
|
||||
Some(string.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_class(&self) -> Option<String> {
|
||||
let mut buffer: [c_char; 2048] = [0; 2048];
|
||||
if unsafe { info_get_class(buffer.as_mut_ptr(), (buffer.len() - 1) as i32) } > 0 {
|
||||
let string = unsafe { CStr::from_ptr(buffer.as_ptr()) };
|
||||
let string = string.to_string_lossy();
|
||||
if !string.is_empty() {
|
||||
Some(string.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_title(&self) -> Option<String> {
|
||||
let mut buffer: [c_char; 2048] = [0; 2048];
|
||||
if unsafe { info_get_title(buffer.as_mut_ptr(), (buffer.len() - 1) as i32) } > 0 {
|
||||
let string = unsafe { CStr::from_ptr(buffer.as_ptr()) };
|
||||
let string = string.to_string_lossy();
|
||||
if !string.is_empty() {
|
||||
Some(string.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
230
espanso-info/src/wayland/native.cpp
Normal file
230
espanso-info/src/wayland/native.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso 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 espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// This file:
|
||||
// Created: 2022-04-04T08:37:16+02:00 by Hendrik G. Seliger (github@hseliger.eu)
|
||||
// Last changes: 2022-04-05T11:46:39+02:00 by Hendrik G. Seliger (github@hseliger.eu)
|
||||
|
||||
// Based on an example given by Ranjit Katuri on https://stackoverflow.com/a/17645247
|
||||
// Uses RapidJSON header-only parser (http://rapidjson.org/)
|
||||
|
||||
// Error handling is quite rudimentary: whenever something goes wrong, the functions simply
|
||||
// return a somewhat generic message instead of the active window's title, class, or command.
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#include "native.h"
|
||||
#include <unistd.h> // for readlink
|
||||
#include <iostream>
|
||||
#include <dbus/dbus.h>
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
|
||||
DBusConnection* conn = NULL;
|
||||
|
||||
#define DB_INTERFACE "org.gnome.Shell.Extensions.WindowsExt"
|
||||
#define DB_DESTINATION "org.gnome.Shell"
|
||||
#define DB_PATH "/org/gnome/Shell/Extensions/WindowsExt"
|
||||
|
||||
// Careful!! Code relies on methods aligning with INFO-numbers below!!
|
||||
#define INFO_TITLE 1
|
||||
#define INFO_EXEC 2
|
||||
#define INFO_WINCLASS 3
|
||||
static const char* methods[] = { "FocusTitle", "FocusPID", "FocusClass" };
|
||||
|
||||
#define MAX_CMD_LINE 120
|
||||
|
||||
static const char* errMessage = "Error retrieving window info. Are you on Gnome and do you have the Window Calls extension installed and active?";
|
||||
|
||||
// Helper function to setup connection
|
||||
int _vsetupconnection()
|
||||
{
|
||||
DBusError err;
|
||||
// initialise the errors
|
||||
dbus_error_init(&err);
|
||||
// connect to session bus
|
||||
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
if (DEBUG > 0) {
|
||||
::perror("Connection Error. ");
|
||||
::perror(err.name);
|
||||
::perror(err.message);
|
||||
}
|
||||
dbus_error_free(&err);
|
||||
}
|
||||
if (NULL == conn) {
|
||||
return(-1);
|
||||
}
|
||||
else {
|
||||
if (DEBUG > 0) {
|
||||
std::cerr << "Connected to session bus\n";
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
//Send method call, Returns NULL on failure, else pointer to reply
|
||||
// DBusMessage* _sendMethodCall(const char* objectpath,
|
||||
// const char* busname,
|
||||
// const char* interfacename,
|
||||
// const char* methodname);
|
||||
DBusMessage* _sendMethodCall(const char* objectpath, const char* busname, const char* interfacename, const char* methodname)
|
||||
{
|
||||
assert(objectpath != NULL); assert(busname != NULL); assert(interfacename != NULL);
|
||||
assert(methodname != NULL); assert(conn != NULL);
|
||||
|
||||
DBusMessage* methodcall = dbus_message_new_method_call(busname,objectpath, interfacename, methodname);
|
||||
|
||||
if (methodcall == NULL) {
|
||||
if (DEBUG > 0) {
|
||||
::perror("Cannot allocate DBus message!");
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
//Now do a sync call
|
||||
DBusPendingCall* pending;
|
||||
DBusMessage* reply;
|
||||
|
||||
//Send and expect reply using pending call object
|
||||
if (!dbus_connection_send_with_reply(conn, methodcall, &pending, -1))
|
||||
{
|
||||
if (DEBUG == 1) {
|
||||
::perror("failed to send message!");
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
dbus_connection_flush(conn);
|
||||
dbus_message_unref(methodcall);
|
||||
methodcall = NULL;
|
||||
|
||||
//Now block on the pending call
|
||||
dbus_pending_call_block(pending);
|
||||
//Get the reply message from the queue
|
||||
reply = dbus_pending_call_steal_reply(pending);
|
||||
//Free pending call handle
|
||||
dbus_pending_call_unref(pending);
|
||||
assert(reply != NULL);
|
||||
|
||||
if(dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
if (DEBUG >0 ) {
|
||||
::perror("Error!");
|
||||
::perror(dbus_message_get_error_name(reply));
|
||||
}
|
||||
dbus_message_unref(reply);
|
||||
reply = NULL;
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
void _getInformation(int infoType, char *buffer, int32_t buffer_size) {
|
||||
// First, ensure we get a connection. Then, which should never happen,
|
||||
// but just in case ensure we have a method for the infoType
|
||||
if ( (_vsetupconnection() == 1) && (infoType <= (int)sizeof(methods)) ) {
|
||||
std::cerr << "Using method " << methods[infoType - 1] << "\n";
|
||||
DBusMessage* reply = _sendMethodCall(DB_PATH, DB_DESTINATION, DB_INTERFACE, methods[infoType - 1]);
|
||||
if(reply != NULL) {
|
||||
DBusMessageIter MsgIter;
|
||||
dbus_message_iter_init(reply, &MsgIter);//msg is pointer to dbus message received
|
||||
|
||||
if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&MsgIter)){
|
||||
char* dbusMsg = NULL;
|
||||
dbus_message_iter_get_basic(&MsgIter, &dbusMsg);
|
||||
|
||||
if (DEBUG > 1) {
|
||||
std::cerr << "Received string: " << dbusMsg << "\n";
|
||||
}
|
||||
switch (infoType) {
|
||||
case INFO_TITLE:
|
||||
// std::cout << "Title: " << (*p)["title"].GetString() << "\n";
|
||||
strncpy(buffer, dbusMsg, buffer_size);
|
||||
break;
|
||||
case INFO_EXEC:
|
||||
{
|
||||
size_t pathLen = snprintf(NULL, 0, "/proc/%s/cmdline", dbusMsg);
|
||||
char* pathBuffer = (char*) malloc(pathLen);
|
||||
sprintf(pathBuffer,"/proc/%s/exe", dbusMsg);
|
||||
// std::cout << "Proc file: " << pathBuffer << "\n";
|
||||
readlink(pathBuffer, buffer, buffer_size);
|
||||
free(pathBuffer);
|
||||
}
|
||||
break;
|
||||
case INFO_WINCLASS:
|
||||
// std::cout << "Class: " << (*p)["class"].GetString() << "\n";
|
||||
strncpy(buffer, dbusMsg, buffer_size);
|
||||
break;
|
||||
default:
|
||||
strncpy(buffer, errMessage, buffer_size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dbus_message_unref(reply); //unref reply
|
||||
} else {
|
||||
if (DEBUG == 1) {
|
||||
::perror("Error! Send Message Call failed!");
|
||||
}
|
||||
strncpy(buffer, errMessage, buffer_size);
|
||||
}
|
||||
// Closing gives error: Applications must not close shared connections - see dbus_connection_close() docs.
|
||||
// dbus_connection_close(conn);
|
||||
|
||||
} else {
|
||||
if (DEBUG > 0) {
|
||||
::perror("Error! Could not get connection to session bus!");
|
||||
}
|
||||
strncpy(buffer, errMessage, buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t info_get_title(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
_getInformation(INFO_TITLE, buffer, buffer_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t info_get_exec(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
_getInformation(INFO_EXEC, buffer, buffer_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t info_get_class(char *buffer, int32_t buffer_size)
|
||||
{
|
||||
_getInformation(INFO_WINCLASS, buffer, buffer_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if DEBUG > 1
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
char outString[MAX_CMD_LINE];
|
||||
|
||||
info_get_title(outString, MAX_CMD_LINE);
|
||||
std::cout << outString << "\n";
|
||||
info_get_exec(outString, MAX_CMD_LINE);
|
||||
std::cout << outString << "\n";
|
||||
info_get_class(outString, MAX_CMD_LINE);
|
||||
std::cout << outString << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
29
espanso-info/src/wayland/native.h
Normal file
29
espanso-info/src/wayland/native.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso 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 espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ESPANSO_INFO_H
|
||||
#define ESPANSO_INFO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" int32_t info_get_title(char * buffer, int32_t buffer_size);
|
||||
extern "C" int32_t info_get_exec(char * buffer, int32_t buffer_size);
|
||||
extern "C" int32_t info_get_class(char * buffer, int32_t buffer_size);
|
||||
|
||||
#endif //ESPANSO_INFO_H
|
Loading…
Reference in New Issue
Block a user