lwouis / alt-tab-macos

Windows alt-tab on macOS
https://alt-tab-macos.netlify.app
GNU General Public License v3.0
10.95k stars 331 forks source link

Add CLI support #371

Open v-braun opened 4 years ago

v-braun commented 4 years ago

First: thanks for this app! I tried HyperSwitch for long time but it was so annoying that the order of windows (most recent window) is based on the active space, if you switch windows between spaces the order is messed up (maybe something that you can to the compare table on your website).

No my question: I wanted to built a Alfred Workflow to list and search within windows, this app would be a good starting point to get all windows if it would have a CLI.

Is there something like this?

lwouis commented 4 years ago

Hi @v-braun! Thanks for sharing this feedback!

I'm not sure i get a clear picture of what that CLI would look like.

Could you please specify it more? Like example commands you would need to build your workflow, and what they output, in more details?

Currently there is none of that. The app is only usable through its GUI

v-braun commented 4 years ago

Hey, what I imagine is a CLI interface to query Windows, Apps and Spaces. For my specific use case only windows are needed.

This interface would be cool:

# list all windows from all spaces
# options: 
# -o / --output (table or json): specify output format
# -s / --space: filter by space 
# -d / --display: filter by display
# -t : filter by title (regex??, fuzzysearch??)

#example: 
alt-tab window list -o json

# output:
[{
   "id": "unique window id",
   "appId": "process id of the app",
   "title": "window title",
   "size": {
      "height": 100,
      "width": 100
   }
}]

# activate a window
#example: 
alt-tab window activate <<window id>>

Other subcommands could be:

lwouis commented 4 years ago

That's interesting! I have a few questions:

Regarding filtering, you wrote:

-s / --space: filter by space

-d / --display: filter by display

-t : filter by title (regex??, fuzzysearch??)

I wonder if filtering wouldn't be better done by the user. Like doing regex search in titles or fuzzysearch could be done by piping grep or fzf, or writting a program that feeds off the JSON output and sorts/filters it.

Do you care about the windows order? Should it be ordered by latest-used like in the graphical AltTab?

I'm wondering if this binary cli you want would be best as part of AltTab.app, or as a standalone binary. Some points to consider:

Another point is that AltTab builds an understanding of windows by listening to changes in apps, windows, user actions, permissions, etc. This means that cli you mention would only be a client, probably to an XPC server. Basically we would split the "system model" as an XPC server, and have a CLI frontend and the current GUI frontend query that backend.

Finally, I'm quite curious, could you describe the Alfred Workflow you are trying to build? I'm curious as to what you will do in Alfred that the current UI doesn't do for you, or couldn't be improved to do

v-braun commented 4 years ago

a lot of questions, but looks like you like the idea wich is great 😀

The commands that I proposed are more general. For my specific workflow I need only a list of windows (id, title) and a subcommand to activate a window (focus).

The workflow that I want to build is:

There is a already such a workflow wich is very slow and ... there we come to the XPC server. I already proposed there such kind of a solution (based on a unix socket or something like this): https://github.com/mandrigin/AlfredSwitchWindows/issues/31#issuecomment-623538569

So let me try to recap your questions:

You said: "to query Windows, Apps and Spaces", but then there is nothing about Apps in the subcommands. Instead there is a subcommand for Displays. I guess you also said that you personally only have interest in Windows?

Youre right, forgot the app

I wonder if filtering wouldn't be better done by the user. Like doing regex search in titles or fuzzysearch could be done by piping grep or fzf, or writting a program that feeds off the JSON output and sorts/filters it.

Right again, I thought that could be optional and maybe could have a better performance if the cli is doing it already

Do you care about the windows order? Should it be ordered by latest-used like in the graphical AltTab? In the workflow it would be good to have a default order, wich is the 'last active' order as in the app. A lot of window switcher cannot handle it well (order is only correct within a space, you did a great job hee). Maybe it is also possible to have a dedicated property on the window object that has the order, so that a consumer can use it

I'm wondering if this binary cli you want would be best as part of AltTab.app, or as a standalone binary. Some points to consider:

I think a server is a great idea, a cli client that can be used from other apps would be very cool to automate things (like automatically rearange windows for coding/design tasks, etc.)

Another point is that AltTab builds an understanding of windows by listening to changes in apps, windows, user actions, permissions, etc. This means that cli you mention would only be a client, probably to an XPC server. Basically we would split the "system model" as an XPC server, and have a CLI frontend and the current GUI frontend query that backend.

YEP! a XPC server and a client would be cool

Finally, I'm quite curious, could you describe the Alfred Workflow you are trying to build? I'm curious as to what you will do in Alfred that the current UI doesn't do for you, or couldn't be improved to do

type: win {query} will return a list of windows that match the query, user can select a window and it get focused.

lwouis commented 4 years ago

If your goal is to get a list of windows, and filter it based on criterias, I think you have rich existing solutions for that right now with Witch and Contexts. Have you tried those perhaps?

v-braun commented 4 years ago

Yes, I tried Contexts for a while but wanted to have it integrated in alfred

lwouis commented 4 years ago

Interesting article about all the IPC solutions on macOS. A bit old but the author is also Alamofire's author and their skill shows in the writting.

ayroblu commented 3 years ago

I was curious if supporting the AppleScript commands would be sufficient here. I don't really understand how the objects and interfaces are defined, but maybe it's a minimal object export of some kind? osascript has a js language interface too so theoretically easier to use for some people. Obvious downside potentially of performance 🤷‍♂️️

lwouis commented 3 years ago

@ayroblu this sounds like equal or more work than a custom cli.

The reason this ticket represents a large amount of work is because it requires a new architecture.

Today, the app is a macOS app (i.e. a .app bundle). It runs a "background app", and presents an icon in then MenuBar. This app process is listening to global events such as key presses, mouse use, windows changes, app changes, preferences changes, etc.

For this ticket, we would need to isolate the core of the app which processes these events, and keeps track of windows and other things. Then we would run this as an XPC process, in the background. Then we would write a new "client" cli app, which you can call from the cli. That app would be very short lived, and would be invoked with something like at list windows. It would then send a request to the XPC process to get the list of windows, and after receiving it, would render it as text in stdout, and terminate.

Writing the new cli is probably easy and fun. Refactoring the current app to act as a background stateful server, is a lot of work. Also there are performance considerations to take into account with the new design. As well as maintenance considerations (i.e. another repo for the cli? How to deal with changes in one or the other repo? Backward compatibility? Forward compatibility? etc)

ayroblu commented 3 years ago

Okay, I'm not really sure how objects get exported for scripting by macOS, it did feel like less work than doing the rearchitecture, but I wouldn't know 🤷‍♂️

lwouis commented 3 years ago

how objects get exported for scripting by macOS

I don't know enough about this. I've never used it. If someone wants to explain and share a work plan, I'm interested.

ayroblu commented 3 years ago

Just as context, how Reminders works:

function run(args) {
  var Reminders = Application("Reminders");
  lists = Reminders.lists;
  for (var i = 0; i < lists.length; ++i) {
    console.log(lists[i].name());
  }
}

Then run osascript -l JavaScript <filename>.js (permissions etc)

Sadly this is extremely slow, like 4 seconds on my mac so I don't think this is a great solution

ayroblu commented 3 years ago

Oh sorry for the repeated messages, I was thinking, the easiest way might actually just to persist it on disk somewhere? Just export a structured json when the underlying data changes? It might be slightly expensive, but not a crazy amount so

lwouis commented 3 years ago

That's a very interesting idea. We would need to define what's stored in more details, but probably a v1 of that feature could export windows with their details like which Space it's on, which screen it's on, the order of recently-used-ness, etc.

A worry is the performance hit it could add. It would probably be an opt-in preference anyway, so that's probably fine. Another question is where that file would be stored. I think most places from Catalina onwards require a System permission to be granted first, which I can tell you from adding permission listening code before, is way harder to deal with than it should be.

I would love to give that a try in a local build, but I think other issues are more important, and I'm basically overwhelmed with the amount of issues at the moment. I even have some PRs open I need to deal with.

@ayroblu @v-braun are you interested in contributing perhaps? It's a nice scope for a first PR on the project

ayroblu commented 3 years ago

After having played with the code, it's actually pretty easy to (theoretically) expose a port to connect to with an API which allows you to do RPC calls. I actually don't think there's much refactoring as such to do, it's all global static variables anyways. My diff is good enough for the alfred use case, but I think for a proper solution, rpc / http would be pretty straightforward. This would be similar to alfred's remote feature or I'm sure other remote systems apis

lwouis commented 3 years ago

Technical infra to communicate between processes is fine I'm sure. As I've said, the hard part is to re-architect the app from a all-in-one, performant monolithic process, into a client + a background server.

People who use the cli probably don't want the normal keyboard/mouse/trackpad event handling to happen. So we can't just add a cli client on top of the existing app. We have to have an architecture like this:

Both of them would communicate with a background server like at-server.

We would probably wrap both experiences in 2 packaging:

It's that separation that creating lots of work. It's a whole new approach with lots of added complexities. It would mean different repositories, releasing the cli package in addition to the .app of today. It would mean 2 distributions to maintain, update, develop. How would updating the cli look like? Another homebrew target maybe only?

Lots of questions, lots of work. It's a whole thing

ayroblu commented 3 years ago

Just to be clear, theoretically you could do that, but I think we'd get pretty far without bothering with most of that.

People who use the cli probably don't want the normal keyboard/mouse/trackpad event handling to happen

Personally I don't think this is a big deal, I think simply having the control to list windows and perform actions from shell while keeping all the existing functionality is perfectly fine

If you really want to create a cli client (I don't think it's necessary, just exposing an API is more than enough I think), monorepos are great, so it could just be another folder.

Personally the lowest effort way is to use some third party (atleast it looks pretty hardcore to roll your own) server, stick a few routes, listen on a port based on a preference, publish an API schema of some kind and be done with it

lwouis commented 3 years ago

I estimate that effort to be multiple months of work. I may be wrong though. PR welcome ;p

Stvad commented 3 years ago

I just had the same Idea that the original author here (wanted to use this as a foundation for an Alfred workflow). Well I really want to accomplish #590, but I thought I can do that myself with the workflow if something like CLI existed 😅.

For other people coming here with the same idea - I've found https://github.com/mandrigin/AlfredSwitchWindows which is similar but has a bunch of issues and not currently supported 🙁.


It's unfortunate that it seems to require a lot of work.

As a hacky approach that might work here for the use-case of Alfred WF - would it be possible to, if a certain setting enabled, regularly (every second/a few seconds) dump the "state of the world" (all the windows in the LRU order) into a file?

The Alfred WF (and other integrating apps) can then read the file and use that info. @lwouis what do you think? UPD: oh there is actually a PR for this 🙈 #761

KhimairaCrypto commented 3 years ago

Adding CLI support will unlock the possibility to use Aldred to switch windows :-). Please expose the same control over CLI.

KhimairaCrypto commented 3 years ago

I just had the same Idea that the original author here (wanted to use this as a foundation for an Alfred workflow). Well I really want to accomplish #590, but I thought I can do that myself with the workflow if something like CLI existed 😅.

For other people coming here with the same idea - I've found https://github.com/mandrigin/AlfredSwitchWindows which is similar but has a bunch of issues and not currently supported 🙁.

It's unfortunate that it seems to require a lot of work.

As a hacky approach that might work here for the use-case of Alfred WF - would it be possible to, if a certain setting enabled, regularly (every second/a few seconds) dump the "state of the world" (all the windows in the LRU order) into a file?

The Alfred WF (and other integrating apps) can then read the file and use that info. @lwouis what do you think? UPD: oh there is actually a PR for this 🙈 #761 I have not been able to find any workflow that provide full window support like alt-tab does!