ci_edit icon indicating copy to clipboard operation
ci_edit copied to clipboard

Change in-memory file permissions if file has been modified by user

Open aaxu opened this issue 7 years ago • 10 comments

Could possibly do this with a background thread that polls for file permissions.

  • If a file is opened and the user has write permission and then the file's permissions gets changed so that the user only has read permissions, we should add [RO] to the file name on top of the program and change the self.isReadOnly variable.
  • If the file originally was originally read only and then the user was given write permissions, we should remove [RO] from the file name and set the self.isReadOnly variable correspondingly.

aaxu avatar Dec 13 '17 16:12 aaxu

Working on this by creating a background thread that will periodically poll. Not too sure about how often I should let it poll.. I was thinking maybe 2-3 seconds since that is around the time it will take for someone for someone to switch back from modifying the file, back to our program. The amount of work that the thread does in polling isn't a lot so I don't think there will be a performance issue, but we will find out once I get something working.

aaxu avatar Dec 14 '17 02:12 aaxu

It would be great to get an event when we lose/gain focus in the terminal. I haven't found that yet (don't know if it's available). Help wanted finding how to get these events.

I agree that polling would work, but I'd prefer to be event based if it's possible.

dschuyler avatar Dec 14 '17 03:12 dschuyler

I've been playing around with a few different tools. There is a python library called watchdog that creates an object and will fire a callback function if it detects a modification for a file. However, this doesn't seem to work if i were to checkout another branch and the file was changed. I'm not sure why this is, it may be because the file gets replaced with another file of the same name, rather than being modified.

I've also found this nice script that can be used for detecting the active window, which you can see here on the last post:

https://stackoverflow.com/questions/46628209/get-the-process-of-the-active-window-with-python-in-linux

I'm not too sure about how this transfers over to other OS, but the problem with this in linux is that it cannot differentiate between two terminals that have the same cwd. It also cannot detect any programs whose interface is inside the terminal window (ie: ci_edit, man, less, vim, etc.). Since this doesn't really work to our need, I'm not too sure about how to use these tools, which is why I started trying it out with the polling.

aaxu avatar Dec 14 '17 03:12 aaxu

So I got a quick implementation done, but I ran into some issues. It seems that all the windows (TopInfo, StatusLine, MessageLine, etc.) are managed by the main thread. My background thread periodically polls the file for its correct stats and so it always has an updated self.isReadOnly variable. However, the windows cannot be rendered since the main thread is blocked by curses.getch().

I think I could probably call the onChange method of each window that needs to be refreshed, but that would increase the overhead of this background thread. I think currently, only TopInfo would need to be refreshed since that displays the '[RO]' message, so it wouldn't be a big deal. However, to work around this, I was thinking that we could maybe find some way to make curses.getch() have its own thread and notify the main thread if it detected a user input? I'm also not too sure how you are making the cursor blink, but maybe we could update the windows along with the cursor.

aaxu avatar Dec 14 '17 19:12 aaxu

How does the program make the cursor blink? I'm trying to find it in the code, but I can't seem to find it. From what I can tell, the background thread infinitely loops and checks for anything in the queue while the main program checks for user input. I tried manually calling topInfo's onChange method or calling the inputWindow's render method, but it doesn't seem to actually refresh the topInfo until a key has been pressed. Do you have any idea of how to manually refresh the screen from a textbuffer? Like self.view.render (which doesn't seem to work)

aaxu avatar Dec 20 '17 16:12 aaxu

Ah, the blinking happens with the terminal (we don't blink the cursor). I've considered blinking/controlling the cursor more, but haven't done it.

The background thread alerts the main thread to changes by putting a messing in the queue and calling os.kill. Search for os.kill in ci_edit/app/background.py to see examples.

dschuyler avatar Dec 20 '17 20:12 dschuyler

Hmm.. then do you know of a way to refresh the screen? My background thread is trying to re-render it, (tried calling the CiProgram and InputWindow's render method, and topInfo's onChange method), but it doesn't seem to be refreshing the screen.

One thing I tried was calling CiProgram.render() (via self.textBuffer.view.host.render()), and this function call is supposed to log a message that says 'screen refresh'. However, it doesn't actually refresh the screen. I know this because if I change permissions of a file 5 times, there are no new messages. However, once I press any key, the window gets refreshed and the message log is updated with 6 'screen refresh' messages (1 from the key stroke and 5 from the file permissions).

aaxu avatar Dec 20 '17 20:12 aaxu

Something to try (I can't really look into this deeply right now, but I can later tonight): Rather than trying to talk to the main thread, put a message into the queue going to the background thread. i.e. rather than setting it up like [background thread] <--> [main thread] <--> [your thread] do [your thread] --> [background thread] <--> [main thread]

Pass the queue to [your thread] when it's created. Whenever [your thread] wants the screen to refresh, put a message in the queue to the [background thread]

There's an existing special message to 'quit' that the [background thread] watches for. Maybe make a new special message like 'redraw' or something. Or maybe send it the same kind a message the [main thread] does, but with an empty command list.

dschuyler avatar Dec 20 '17 22:12 dschuyler

I should almost have a rough implementation of this done.

aaxu avatar Jan 04 '18 03:01 aaxu

In commandLoop, I see that the bg thread is handled twice. Is there a reason for this, such as handling two bg requests per loop?

aaxu avatar Jan 04 '18 17:01 aaxu