Initial espanso-info version for Wayland

This commit is contained in:
Hendrik G. Seliger 2022-04-06 07:34:48 +02:00
parent 5df94b5031
commit 3bc4f5a105
5 changed files with 366 additions and 12 deletions

View File

@ -49,7 +49,20 @@ fn cc_config() {
println!("cargo:rustc-link-lib=dylib=stdc++"); println!("cargo:rustc-link-lib=dylib=stdc++");
println!("cargo:rustc-link-lib=dylib=X11"); println!("cargo:rustc-link-lib=dylib=X11");
} else { } 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");
} }
} }

View 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;
}

View File

@ -17,23 +17,78 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * 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 crate::{AppInfo, AppInfoProvider};
use self::ffi::{info_get_class, info_get_exec, info_get_title};
mod ffi;
pub(crate) struct WaylandAppInfoProvider {} pub(crate) struct WaylandAppInfoProvider {}
impl WaylandAppInfoProvider { impl WaylandAppInfoProvider {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
} }
impl AppInfoProvider for WaylandAppInfoProvider { impl AppInfoProvider for WaylandAppInfoProvider {
// TODO: can we read these info on Wayland? fn get_info(&self) -> AppInfo {
fn get_info(&self) -> AppInfo { AppInfo {
AppInfo { title: self.get_title(),
title: None, class: self.get_class(),
exec: None, exec: self.get_exec(),
class: None, }
} }
} }
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
}
}
} }

View 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

View 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