Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add screenshot for single screen #32

Merged
merged 16 commits into from
Jul 30, 2022
Merged

Add screenshot for single screen #32

merged 16 commits into from
Jul 30, 2022

Conversation

SushiWaUmai
Copy link
Contributor

Resolves #30

@SushiWaUmai
Copy link
Contributor Author

Sorry for the diffs, my formatter did that automatically.

Copy link
Member

@Lonami Lonami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming it builds changes look fine, although perhaps we should separate the format and changes into two commits.

src/main.rs Outdated
@@ -1,4 +1,4 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// This Source Code Form is subject to the terms of the Mozilla& Public
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is necessary.

@pickfire
Copy link
Collaborator

pickfire commented Jul 23, 2022

It doesn't seem to update the docs, other people would have to look at the code to know.

@9ary
Copy link
Member

9ary commented Jul 23, 2022

It doesn't seem to update the docs, other people would have to look at the code to know.

They did add help text for the new flag, but it should indeed be copied over to the README.
I'm blind. This doesn't seem to add a new flag at all?

@bb010g
Copy link
Contributor

bb010g commented Jul 23, 2022

Sorry for the diffs, my formatter did that automatically.

Please split the reformatting out into a separate, dedicated commit.

src/main.rs Outdated
@@ -32,7 +32,7 @@ fn run() -> i32 {

let mut opts = Options::new();
opts.optopt("i", "id", "Window to capture", "ID");
opts.optopt("g", "geometry", "Area to capture", "WxH+X+Y");
opts.optflagopt("g", "geometry", "Area to capture", "WxH+X+Y");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see what you did here. This means that -g without specifying a region would capture the screen where the cursor is. This isn't good, please add a separate flag instead, and document it properly.

Copy link
Contributor

@bb010g bb010g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @9ary. Please do not introduce options that optionally take values to this CLI. The functionality looks fine, if implemented differently.

@SushiWaUmai
Copy link
Contributor Author

Hi, I appreciate the reviews, my suggestion would be to revert the commit once and

  1. separate the commits from functionality and formatting
  2. introduce the new flag
  3. add more documentation

Unfortunately I do not have much time, and therefore I cannot implement it right away.

Copy link
Member

@9ary 9ary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good overall.

Another concern I have is you don't seem to handle passing both -g and -s gracefully. Currently, -s will override -g unconditionally. I would prefer to either switch that around, or take the intersection.

src/main.rs Outdated
let sel = match matches.opt_str("g") {

let mut sel: util::Rect;
sel = match matches.opt_str("g") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think splitting this line in two or even specifying the type is strictly necessary here. Is it?

src/main.rs Outdated
}
};

let screen_rects = match display.get_screen_rects(display.get_default_root()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces an additional call to get_screen_rects which is a little inefficient. It should be cached, or better yet lazily initialized so it only gets called when needed. std::Lazy is currently experimental, so we'll need to find a crate for this.
Since get_screen_rects returns an iterator, you'll need to collect it into a vector, and then make the masking step reuse that. Maybe removing the iterator altogether would be a good idea. I believe I originally wrote it this way to avoid superfluous allocations, but that's already out of the window, so there's no point in keeping it.

We also already cache the result of get_default_root during initialization. You should definitely use that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for this part, I think it should be fine if we cache it for now using a vector, I think we can still change that later on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, do that, and adapt the masking step to reuse the same vector. Lazy evaluation might not even be necessary considering that masking always happens so forget about that.

src/main.rs Outdated
Comment on lines 142 to 148
sel = match screen_rects.enumerate().find(|(_, r)| r.contains(cursor)) {
Some((_, r)) => r,
None => {
eprintln!("Failed to find screen containing cursor");
return 1;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright so under normal circumstances this shouldn't fail. That said, maybe we should only bother to mutate sel if something is found? Honestly not sure what the best strategy is here. This seems sound enough all things considered.

src/util.rs Outdated Show resolved Hide resolved
src/xwrap.rs Outdated
Comment on lines 129 to 152
pub fn get_cursor_position(&self) -> Option<util::Pos> {
let mut x = 0;
let mut y = 0;

let (mut win_x, mut win_y, mut mask) = (0, 0, 0);
let mut root_win = self.get_default_root();

unsafe {
xlib::XQueryPointer(
self.handle,
root_win,
&mut root_win,
&mut root_win,
&mut x,
&mut y,
&mut win_x,
&mut win_y,
&mut mask,
);
}


Some(util::Pos { x, y })
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns an Option, but it doesn't appear to handle the failure case. If XQueryPointer is fallible, then it should return None on failure. Otherwise, get rid of the Option.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I see another call to get_default_root. This method should probably be implemented for xlib::Window instead, or take the root window as another parameter.

src/main.rs Outdated Show resolved Hide resolved
@bb010g
Copy link
Contributor

bb010g commented Jul 25, 2022

Another concern I have is you don't seem to handle passing both -g and -s gracefully. Currently, -s will override -g unconditionally. I would prefer to either switch that around, or take the intersection.

On this note, I would highly recommend at least reading the Command Line Arguments section of Fuchsia's Command-line Tools Rubric. Here's a relevant section from it:

Mutually Exclusive Options

Some options don't make sense with other options. We call the options mutually exclusive.

Passing mutually exclusive options is considered a user error. When this occurs the tool will do one of the following:

  • Write an error message explaining the issue and exit with a non-zero result code; doing no work (i.e. there was no data changed as a result of the call). This is the expected handling, so no further documentation or notes are required.
  • Prioritize one option over another. E.g. "passing -z will override -y". In this case the handling will be documented in the --help output.
  • Other handling is possible (first takes precedence or last takes precedence or something else) though this is discouraged. In this case the handling will be documented in the Description, Options, and Notes; though "See Notes" may be used in Description and Options with the full write-up in Notes.

@9ary
Copy link
Member

9ary commented Jul 25, 2022

Erroring out is also an acceptable solution yes. Either way. the conflict behavior should be documented.

Copy link
Member

@9ary 9ary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think -s should also conflict with -i. -g + -i makes sense, and we just take the intersection there, but that wouldn't make sense with -s.

src/xwrap.rs Outdated
Comment on lines 150 to 172
pub fn get_cursor_position(&self, mut root_win: xlib::Window) -> util::Point {
let mut x = 0;
let mut y = 0;

let (mut win_x, mut win_y, mut mask) = (0, 0, 0);

unsafe {
xlib::XQueryPointer(
self.handle,
root_win,
&mut root_win,
&mut root_win,
&mut x,
&mut y,
&mut win_x,
&mut win_y,
&mut mask,
);
}

util::Point { x, y }
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So according to the documentation, XQueryPointer is fallible. https://tronche.com/gui/x/xlib/window-information/XQueryPointer.html

The first failure mode is a BadWindow error if you pass in an invalid window. For our use that would be a programming error so you can just make that a panic. (actually xlib will very likely do that itself so maybe it's not necessary, try passing it some random integer and see what happens)

It looks like the other failure case is related to having multiple X screens (not the same as multiple monitors). That is a very uncommon configuration that we shouldn't bother supporting. Regardless, you should re-introduce the Option and the error message for when XQueryPointer returns false.

Lastly, the root_win argument should just be called window, and it shouldn't be mutable. This is a big C-ism that we don't have to deal with in Rust. If you want to use the values XQueryPointer puts in there, you should put them in the return struct. It's unnecessary in this case though, so you should just throw root_return and child_return away.

I do wonder, what happens if you pass 0 as the window argument? If it works, then we should still get the cursor position relative to the root window and you can get rid of the window argument. Otherwise, win_x and win_y are the values you should be returning, as they are relative to the window you're passing in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll make sure that I get that done soon, unfortunately this is the first project I worked on with rust so I'll have to take a closer look to the error handing part.

src/main.rs Outdated
@@ -133,20 +133,20 @@ fn run() -> i32 {
},
};

let screen_rects: Vec<util::Rect> = match display.get_screen_rects(root) {
Some(r) => r.map(|r| r.clone()).collect(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Some(r) => r.map(|r| r.clone()).collect(),
Some(r) => r.cloned().collect(),

Some(r) => r.map(|r| r.clone()).collect(),
None => {
eprintln!("Failed to get screen rects");
return 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that shotgun will always bail out if screens can't be enumerated, even when it's not strictly required. XRandR should be available on pretty much anything these days, do we care enough to support the failure case?

@SushiWaUmai
Copy link
Contributor Author

Alright I feel like I fixed all the stuff mentioned, did I miss anything?

@9ary
Copy link
Member

9ary commented Jul 30, 2022

I'm still not sure the usage of XQueryPointer is correct, but I currently don't have the energy to continue the back and forth on this pull request (for reasons unrelated). If it works in its current state then I think it's okay to merge this as it is and we can fix that wrapper later.

I'll leave it up to someone who can actually test this to merge the PR and prepare a new release.

Copy link
Member

@Lonami Lonami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still some formatting changes but I don't think that's worth fighting over. Implementation seems fine if it gets the job done, Most if not all concerns have also been fixed so with @9ary's approval I'm going to go ahead and merge it. Thanks!

Copy link
Member

@Lonami Lonami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I meant to approve, not request changes.)

@Lonami Lonami merged commit 97ecf32 into neXromancers:master Jul 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Taking screenshot of one display.
5 participants