blob: b7f33a8208c363ceabd2c963e137c6eb997b5ac8 [file] [log] [blame] [raw]
/*
Copyright 2015-2023 Rivoreo
This program 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 2 of the License, or (at
your option) any later version.
This program 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.
*/
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
#if HAVE_SYSTEM_CONFIGURATION
using System.Configuration;
#else
using Rivoreo.Configurations;
#endif
namespace Rivoreo.ScreenShooter {
class X11 {
private struct XWindowAttributes {
public int x;
public int y;
public int width;
public int height;
public int border_width;
public int depth;
public IntPtr visual;
public IntPtr root;
public int c_class;
public int bit_gravity;
public int win_gravity;
public int backing_store;
public IntPtr backing_planes;
public IntPtr backing_pixel;
public bool save_under;
public IntPtr colormap;
public bool map_installed;
public int map_state;
public IntPtr all_event_masks;
public IntPtr your_event_mask;
public IntPtr do_not_propagate_mask;
public bool override_direct;
public IntPtr screen;
}
[DllImport("libX11.so.6")]
private static extern IntPtr XOpenDisplay(String display_name);
[DllImport("libX11.so.6")]
private static extern int XCloseDisplay(IntPtr display);
[DllImport("libX11.so.6")]
private static extern void XGetInputFocus(IntPtr display, out UIntPtr focus, out int revert);
[DllImport("libX11.so.6")]
private static extern int XQueryTree(IntPtr display, UIntPtr window, out UIntPtr root, out UIntPtr parent, out IntPtr children, out uint nchildren);
[DllImport("libX11.so.6")]
private static extern void XFree(IntPtr p);
[DllImport("libX11.so.6")]
private static extern int XGetWindowAttributes(IntPtr display, UIntPtr window, out XWindowAttributes attributes);
public static Rectangle GetFocusedRectangle() {
IntPtr display = XOpenDisplay(null);
if(display == IntPtr.Zero) return new Rectangle();
UIntPtr window;
int revert;
XGetInputFocus(display, out window, out revert);
UIntPtr root;
UIntPtr parent = window;
IntPtr children; // Unused array of Window
uint nchildren;
while(XQueryTree(display, window, out root, out parent, out children, out nchildren) != 0) {
if(children != IntPtr.Zero) XFree(children);
if(parent == root) break;
window = parent;
}
XWindowAttributes attr;
Rectangle rectangle = XGetWindowAttributes(display, window, out attr) == 0 ?
new Rectangle() : new Rectangle(attr.x, attr.y, attr.width, attr.height);
XCloseDisplay(display);
return rectangle;
}
[StructLayout(LayoutKind.Sequential)]
private struct XSelectionClearEvent {
public int type;
public UIntPtr serial;
[MarshalAs(UnmanagedType.Bool)] public bool send_event;
public IntPtr display;
public UIntPtr window;
public UIntPtr selection;
public UIntPtr time;
}
[StructLayout(LayoutKind.Sequential)]
private struct XSelectionRequestEvent {
public int type;
public UIntPtr serial;
[MarshalAs(UnmanagedType.Bool)] public bool send_event;
public IntPtr display;
public UIntPtr owner;
public UIntPtr requestor;
public UIntPtr selection;
public UIntPtr target;
public UIntPtr property;
public UIntPtr time;
}
[StructLayout(LayoutKind.Sequential)]
private struct XSelectionEvent {
public int type;
public UIntPtr serial;
[MarshalAs(UnmanagedType.Bool)] public bool send_event;
public IntPtr display;
public UIntPtr requestor;
public UIntPtr selection;
public UIntPtr target;
public UIntPtr property;
public UIntPtr time;
}
[StructLayout(LayoutKind.Sequential)]
private struct XEventPad {
internal IntPtr pad_0;
internal IntPtr pad_1;
internal IntPtr pad_2;
internal IntPtr pad_3;
internal IntPtr pad_4;
internal IntPtr pad_5;
internal IntPtr pad_6;
internal IntPtr pad_7;
internal IntPtr pad_8;
internal IntPtr pad_9;
internal IntPtr pad_10;
internal IntPtr pad_11;
internal IntPtr pad_12;
internal IntPtr pad_13;
internal IntPtr pad_14;
internal IntPtr pad_15;
internal IntPtr pad_16;
internal IntPtr pad_17;
internal IntPtr pad_18;
internal IntPtr pad_19;
internal IntPtr pad_20;
internal IntPtr pad_21;
internal IntPtr pad_22;
internal IntPtr pad_23;
}
[StructLayout(LayoutKind.Explicit)]
private struct XEvent {
[FieldOffset(0)] public int type;
[FieldOffset(0)] public XSelectionClearEvent selection_clear_event;
[FieldOffset(0)] public XSelectionRequestEvent selection_request_event;
[FieldOffset(0)] public XSelectionEvent selection_event;
[FieldOffset(0)] internal XEventPad pad;
}
private const uint XA_ATOM = 4;
private const int SelectionClear = 29;
private const int SelectionRequest = 30;
private const int SelectionNotify = 31;
private const int PropModeReplace = 0;
private static Thread selection_owner_thread;
private static Image selection_image;
private static IntPtr selection_owner_display;
private static UIntPtr selection_owner;
private static bool quiting;
[DllImport("libX11.so.6")]
private static extern UIntPtr XDefaultRootWindow(IntPtr display);
[DllImport("libX11.so.6")]
private static extern UIntPtr XCreateSimpleWindow(IntPtr display, UIntPtr parent, int x, int y, uint width, uint height, uint border_width, UIntPtr border, UIntPtr background);
[DllImport("libX11.so.6")]
private static extern UIntPtr XInternAtom(IntPtr display, String name, [MarshalAs(UnmanagedType.Bool)] bool only_if_exists);
[DllImport("libX11.so.6")]
private static extern void XSetSelectionOwner(IntPtr display, UIntPtr selection, UIntPtr owner, UIntPtr time);
[DllImport("libX11.so.6")]
private static extern IntPtr XGetAtomName(IntPtr display, UIntPtr atom);
[DllImport("libX11.so.6")]
private static extern void XChangeProperty(IntPtr display, UIntPtr window, UIntPtr property, UIntPtr type, int format, int mode, byte[] data, int nelements);
[DllImport("libX11.so.6")]
private static extern void XFlush(IntPtr display);
[DllImport("libX11.so.6")]
private static extern void XNextEvent(IntPtr display, out XEvent e);
[DllImport("libX11.so.6")]
private static extern int XSendEvent(IntPtr display, UIntPtr window, [MarshalAs(UnmanagedType.Bool)] bool propagate, IntPtr event_mask, ref XEvent e);
private static void store_perperty(IntPtr display, UIntPtr window, UIntPtr perperty, UIntPtr target, ImageFormat format) {
using(MemoryStream buffer = new MemoryStream()) {
selection_image.Save(buffer, format);
XChangeProperty(display, window, perperty, target, 8, PropModeReplace,
buffer.ToArray(), (int)buffer.Length);
}
}
private static void send_selection_event(XSelectionRequestEvent selection_request_event, UIntPtr property) {
XSelectionEvent selection_event = new XSelectionEvent() {
type = SelectionNotify,
requestor = selection_request_event.requestor,
selection = selection_request_event.selection,
target = selection_request_event.target,
property = property,
time = selection_request_event.time
};
XEvent e = new XEvent() { selection_event = selection_event };
XSendEvent(selection_request_event.display,
selection_request_event.requestor, true, IntPtr.Zero, ref e);
}
private static void run_selection_owner() {
IntPtr display = XOpenDisplay(null);
if(display == IntPtr.Zero) return;
UIntPtr selection_type = XInternAtom(display, "CLIPBOARD", false);
if(selection_type == UIntPtr.Zero) goto cleanup;
UIntPtr targets_target = XInternAtom(display, "TARGETS", false);
UIntPtr png_target = XInternAtom(display, "image/png", true);
UIntPtr bmp_target = XInternAtom(display, "image/bmp", true);
UIntPtr jpeg_target = XInternAtom(display, "image/jpeg", true);
UIntPtr root_window = XDefaultRootWindow(display);
UIntPtr owner = XCreateSimpleWindow(display, root_window, -1, -1, 1, 1, 0, UIntPtr.Zero, UIntPtr.Zero);
XFlush(display);
XSetSelectionOwner(display, selection_type, owner, UIntPtr.Zero);
XFlush(display);
selection_owner_display = display;
selection_owner = owner;
do {
XEvent e;
XNextEvent(display, out e);
switch(e.type) {
case SelectionClear:
Console.Error.WriteLine("lost selection");
goto cleanup;
case SelectionRequest:
Console.Error.WriteLine("got selection request from {0}, target {1}",
e.selection_request_event.requestor, e.selection_request_event.target);
UIntPtr perperty;
if(e.selection_request_event.property == UIntPtr.Zero) {
perperty = UIntPtr.Zero;
} else if(e.selection_request_event.target == targets_target) {
byte[] buffer = new byte[4 * UIntPtr.Size];
if(UIntPtr.Size == 4) {
Array.Copy(BitConverter.GetBytes((uint)targets_target), 0, buffer, 0, 4);
Array.Copy(BitConverter.GetBytes((uint)png_target), 0, buffer, 4, 4);
Array.Copy(BitConverter.GetBytes((uint)bmp_target), 0, buffer, 8, 4);
Array.Copy(BitConverter.GetBytes((uint)jpeg_target), 0, buffer, 12, 4);
} else {
Array.Copy(BitConverter.GetBytes((ulong)targets_target), 0, buffer, 0, 8);
Array.Copy(BitConverter.GetBytes((ulong)png_target), 0, buffer, 8, 8);
Array.Copy(BitConverter.GetBytes((ulong)bmp_target), 0, buffer, 16, 8);
Array.Copy(BitConverter.GetBytes((ulong)jpeg_target), 0, buffer, 24, 8);
}
perperty = e.selection_request_event.property;
XChangeProperty(display, e.selection_request_event.requestor,
perperty, (UIntPtr)XA_ATOM, 32, PropModeReplace,
buffer, buffer.Length / UIntPtr.Size);
} else if(e.selection_request_event.target == png_target) {
perperty = e.selection_request_event.property;
store_perperty(display, e.selection_request_event.requestor,
perperty, png_target, ImageFormat.Png);
} else if(e.selection_request_event.target == bmp_target) {
perperty = e.selection_request_event.property;
store_perperty(display, e.selection_request_event.requestor,
perperty, bmp_target, ImageFormat.Bmp);
} else if(e.selection_request_event.target == jpeg_target) {
perperty = e.selection_request_event.property;
store_perperty(display, e.selection_request_event.requestor,
perperty, jpeg_target, ImageFormat.Jpeg);
} else {
perperty = UIntPtr.Zero;
}
send_selection_event(e.selection_request_event, perperty);
break;
}
} while(!quiting);
cleanup:
XCloseDisplay(display);
}
public static bool QuitSelectionOwnerThread(bool wait_ok) {
if(selection_owner_thread == null) return true;
if(!selection_owner_thread.IsAlive) return true;
quiting = true;
//selection_owner_thread.Interrupt();
XEvent e = new XEvent() {
selection_clear_event = new XSelectionClearEvent() {
type = SelectionClear, display = selection_owner_display
}
};
XSendEvent(selection_owner_display, selection_owner, true, IntPtr.Zero,
ref e);
XFlush(selection_owner_display);
return !wait_ok || selection_owner_thread.Join(100);
}
public static void CopyImage(Image image) {
QuitSelectionOwnerThread(false);
selection_image = image;
selection_owner_thread = new Thread(run_selection_owner);
selection_owner_thread.Start();
}
}
class SelectRectangleForm : Form {
public SelectRectangleForm() {
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.Manual;
//WindowState = FormWindowState.Maximized;
TopMost = true;
Cursor = Cursors.Cross;
DoubleBuffered = true;
ShowInTaskbar = false;
Location = SystemInformation.VirtualScreen.Location;
Size = SystemInformation.VirtualScreen.Size;
BackgroundImage = new Bitmap(Size.Width, Size.Height);
Graphics graphics = Graphics.FromImage(BackgroundImage);
graphics.CopyFromScreen(Location, new Point(), Size);
}
private Point initial_location;
private Rectangle selection;
public Point SelectionLocation {
get { return selection.Location; }
}
/*
public Size SelectionSize {
get { return selection.Size; }
}
*/
public Bitmap Bitmap {
get {
Bitmap bitmap = (Bitmap)BackgroundImage;
if(selection.X < 0) {
selection.Width += selection.X;
selection.X = 0;
}
if(selection.Y < 0) {
selection.Height += selection.Y;
selection.Y = 0;
}
if(selection.Width <= 0 || selection.Height <= 0) return null;
if(selection.Right > bitmap.Width) {
selection.Width = bitmap.Width - selection.X;
}
if(selection.Bottom > bitmap.Height) {
selection.Height = bitmap.Height - selection.Y;
}
return bitmap.Clone(selection, bitmap.PixelFormat);
}
}
protected override void OnPaintBackground(PaintEventArgs a) {
ColorMatrix color_matrix = new ColorMatrix();
color_matrix.Matrix00 = 0.5F;
color_matrix.Matrix11 = 0.5F;
color_matrix.Matrix22 = 0.5F;
color_matrix.Matrix33 = 1F;
color_matrix.Matrix44 = 1F;
ImageAttributes attr = new ImageAttributes();
attr.ClearColorMatrix();
attr.SetColorMatrix(color_matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
a.Graphics.DrawImage(BackgroundImage, DisplayRectangle, 0, 0, Width, Height,
GraphicsUnit.Pixel, attr);
if(!selection.IsEmpty) {
a.Graphics.DrawImage(BackgroundImage, selection, selection,
GraphicsUnit.Pixel);
}
}
protected override void OnPaint(PaintEventArgs a) {
using(Pen pen = new Pen(Color.White, 1)) {
a.Graphics.DrawRectangle(pen, selection);
}
}
protected override void OnMouseDown(MouseEventArgs a) {
if(a.Button != MouseButtons.Left) return;
initial_location = a.Location;
selection = new Rectangle(initial_location, new Size(0, 0));
}
protected override void OnMouseMove(MouseEventArgs a) {
if(a.Button != MouseButtons.Left) return;
if(a.X < initial_location.X) {
selection.X = a.X;
selection.Width = initial_location.X - a.X;
} else {
selection.Width = a.X - initial_location.X;
}
if(a.Y < initial_location.Y) {
selection.Y = a.Y;
selection.Height = initial_location.Y - a.Y;
} else {
selection.Height = a.Y - initial_location.Y;
}
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs a) {
Console.Error.WriteLine(selection);
DialogResult = selection.Width > 0 && selection.Height > 0 ?
DialogResult.OK : DialogResult.Abort;
Close();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keys) {
if(keys == Keys.Escape) {
DialogResult = DialogResult.Cancel;
Close();
return true;
}
return base.ProcessCmdKey(ref msg, keys);
}
}
enum ShootTarget {
FULL_SCREEN, ACTIVE_WINDOW, SELECT_RECTANGLE, SELECT_WINDOW, DYNAMIC_SELECT_RECTANGLE, DYNAMIC_SELECT_WINDOW
}
class MainForm : Form {
#if HAVE_SYSTEM_CONFIGURATION
class GeneralSection : ConfigurationSection {
/*
public new Object this[String key] {
get { return base[key]; }
set { base[key] = value; }
}
*/
[ConfigurationProperty("SavePath", DefaultValue = null)]
public String SavePath {
get { return base["SavePath"] as String; }
set { base["SavePath"] = value; }
}
[ConfigurationProperty("Delay", DefaultValue = 0)]
[IntegerValidator(MinValue = 0)]
public int Delay {
get {
Object value = base["Delay"];
return value == null ? 0 : (int)value;
}
set { base["Delay"] = value; }
}
}
#endif
public MainForm(ShootTarget target) {
Bitmap bitmap = shoot_screen(target);
if(bitmap == null) Application.Exit();
SuspendLayout();
main_panel = new TableLayoutPanel();
main_panel.SuspendLayout();
main_panel.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
main_panel.ColumnCount = 4;
main_panel.RowCount = 5;
main_panel.ColumnStyles.Add(new ColumnStyle());
main_panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 90));
main_panel.ColumnStyles.Add(new ColumnStyle());
main_panel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 90));
//main_panel.BorderStyle = BorderStyle.FixedSingle;
预览区域 = new PictureBox();
预览区域.Dock = DockStyle.Fill;
预览区域.Location = new Point(20, 10);
预览区域.AutoSize = true;
预览区域.Image = bitmap;
预览区域.SizeMode = PictureBoxSizeMode.Zoom;
main_panel.Controls.Add(预览区域, 0, 0);
main_panel.SetColumnSpan(预览区域, 4);
//main_panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
main_panel.RowStyles.Add(new RowStyle(SizeType.Percent, 99));
file_name_label = new Label();
file_name_label.Anchor = AnchorStyles.Right;
file_name_label.AutoSize = true;
file_name_label.Text = "File name";
main_panel.Controls.Add(file_name_label, 0, 1);
file_name_text_box = new TextBox();
//file_name_text_box.AutoSize = true;
file_name_text_box.Dock = DockStyle.Fill;
DateTime dt = DateTime.Now;
file_name_text_box.Text =
"screenshot." + dt.ToString("yyyy-MM-dd.HH-mm-ss") + ".png";
file_name_text_box.TabIndex = 1;
main_panel.Controls.Add(file_name_text_box, 1, 1);
main_panel.SetColumnSpan(file_name_text_box, 3);
main_panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 32));
save_path_label = new Label();
save_path_label.Anchor = AnchorStyles.Right;
save_path_label.AutoSize = true;
save_path_label.Text = "Save to";
main_panel.Controls.Add(save_path_label, 0, 2);
save_path_text_box = new TextBox();
//save_path_text_box.AutoSize = true;
save_path_text_box.Dock = DockStyle.Fill;
save_path_text_box.Text = Application.StartupPath;
save_path_text_box.TabIndex = 2;
main_panel.Controls.Add(save_path_text_box, 1, 2);
main_panel.SetColumnSpan(save_path_text_box, 3);
main_panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 32));
delay_label = new Label();
delay_label.Anchor = AnchorStyles.Right;
delay_label.AutoSize = true;
delay_label.Width = 40;
delay_label.Text = "Delay";
main_panel.Controls.Add(delay_label, 0, 3);
delay_box = new NumericUpDown();
delay_box.AutoSize = true;
//delay_box. = 0;
delay_box.TabIndex = 3;
main_panel.Controls.Add(delay_box, 1, 3);
target_label = new Label();
target_label.Anchor = AnchorStyles.Right;
target_label.AutoSize = true;
target_label.Text = "Target";
main_panel.Controls.Add(target_label, 2, 3);
target_box = new ComboBox();
target_box.AutoSize = true;
target_box.DropDownStyle = ComboBoxStyle.DropDownList;
target_box.Items.AddRange(new Object[] {
"Full screen", "Active window", "Select region", "Select window"
});
target_box.SelectedIndex = (int)target;
target_box.TabIndex = 4;
main_panel.Controls.Add(target_box, 3, 3);
main_panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 32));
button_panel = new FlowLayoutPanel();
button_panel.SuspendLayout();
button_panel.AutoSize = true;
button_panel.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
button_panel.FlowDirection = FlowDirection.LeftToRight;
button_panel.WrapContents = false;
//button_panel.BorderStyle = BorderStyle.FixedSingle;
left_button_panel = new FlowLayoutPanel();
left_button_panel.SuspendLayout();
left_button_panel.AutoSize = true;
left_button_panel.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
left_button_panel.Dock = DockStyle.Fill;
left_button_panel.FlowDirection = FlowDirection.LeftToRight;
left_button_panel.WrapContents = false;
left_button_panel.Margin = new Padding(0);
重新截图按钮 = new Button();
重新截图按钮.Text = "&New";
重新截图按钮.Width = 52;
重新截图按钮.TabIndex = 6;
重新截图按钮.Click += 重新截图;
left_button_panel.Controls.Add(重新截图按钮);
退出按钮 = new Button();
//退出按钮.Anchor = AnchorStyles.Left | AnchorStyles.Right;
退出按钮.Text = "&Quit";
//退出按钮.Location = new Point(20, 124);
退出按钮.Width = 52;
退出按钮.TabIndex = 7;
退出按钮.Click += Quit;
left_button_panel.Controls.Add(退出按钮);
CancelButton = 退出按钮;
left_button_panel.ResumeLayout();
button_panel.Controls.Add(left_button_panel);
right_button_panel = new FlowLayoutPanel();
right_button_panel.SuspendLayout();
right_button_panel.AutoSize = true;
right_button_panel.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
right_button_panel.Dock = DockStyle.Fill;
right_button_panel.FlowDirection = FlowDirection.LeftToRight;
right_button_panel.WrapContents = false;
right_button_panel.Margin = new Padding(0);
复制按钮 = new Button();
复制按钮.Text = "&Copy";
复制按钮.Width = 52;
复制按钮.TabIndex = 5;
复制按钮.Click += Copy;
right_button_panel.Controls.Add(复制按钮);
保存按钮 = new Button();
保存按钮.Text = "&Save";
保存按钮.Width = 52;
//保存按钮.Location = new Point(50, 124);
保存按钮.TabIndex = 0;
保存按钮.Click += Save;
right_button_panel.Controls.Add(保存按钮);
AcceptButton = 保存按钮;
right_button_panel.ResumeLayout();
button_panel.Controls.Add(right_button_panel);
button_panel.Height = 32;
button_panel.ResumeLayout();
main_panel.Controls.Add(button_panel, 0, 4);
main_panel.SetColumnSpan(button_panel, 4);
main_panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 40));
main_panel.ResumeLayout();
Controls.Add(main_panel);
Text = "Screen Shooter";
ClientSize = new Size(240, 260);
AutoScaleMode = AutoScaleMode.Font;
//AutoSize = true;
FormClosed += 清理;
try {
装载配置档();
#if HAVE_SYSTEM_CONFIGURATION
} catch(ConfigurationErrorsException e) {
#else
} catch(IOException e) {
#endif
Console.Error.WriteLine(e);
}
ResumeLayout();
main_panel.ClientSize = ClientSize - new Size(2, 2);
if(预览区域.Width > main_panel.Width - 8) {
预览区域.Width = main_panel.Width - 8;
}
save_path_text_box.Width = main_panel.Width - 32;
CenterToScreen();
}
private Configuration config;
#if HAVE_SYSTEM_CONFIGURATION
private GeneralSection config_section;
#endif
private TableLayoutPanel main_panel;
private FlowLayoutPanel button_panel;
private FlowLayoutPanel left_button_panel;
private FlowLayoutPanel right_button_panel;
private PictureBox 预览区域;
private Label file_name_label;
private TextBox file_name_text_box;
private Label save_path_label;
private TextBox save_path_text_box;
private Label delay_label;
internal NumericUpDown delay_box;
private Label target_label;
private ComboBox target_box;
private Button 重新截图按钮;
private Button 退出按钮;
private Button 复制按钮;
private Button 保存按钮;
private bool has_used_x11_selection = false;
private Bitmap shoot_screen(ShootTarget target) {
Point location;
Size size;
switch(target) {
case ShootTarget.FULL_SCREEN:
location = SystemInformation.VirtualScreen.Location;
size = SystemInformation.VirtualScreen.Size;
break;
case ShootTarget.ACTIVE_WINDOW:
Rectangle rectangle = X11.GetFocusedRectangle();
location = rectangle.Location;
size = rectangle.Size;
break;
case ShootTarget.SELECT_RECTANGLE:
using(SelectRectangleForm f = new SelectRectangleForm()) {
if(f.ShowDialog() != DialogResult.OK) return null;
return f.Bitmap;
}
case ShootTarget.SELECT_WINDOW:
throw new NotImplementedException();
//break;
case ShootTarget.DYNAMIC_SELECT_RECTANGLE:
//select_rectangle(out location, out size);
throw new NotImplementedException();
case ShootTarget.DYNAMIC_SELECT_WINDOW:
throw new NotImplementedException();
default:
throw new ArgumentOutOfRangeException();
}
Bitmap bitmap = new Bitmap(size.Width, size.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(location, new Point(), size);
return bitmap;
}
private void 装载配置档() {
#if HAVE_SYSTEM_CONFIGURATION
config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming);
config_section = config.Sections["General"] as GeneralSection;
if(config_section == null) {
config_section = new GeneralSection();
config.Sections.Add("General", config_section);
Console.Error.WriteLine(config_section.ElementInformation.Properties.Count);
Console.Error.WriteLine(config_section.GetType().GetProperties().Length);
} else {
/*
Object value = config_section["SavePath"];
if(value is String) save_path_text_box.Text = (String)value;
value = config_section["Delay"];
if(value is int) delay_box.Value = (int)value;
*/
if(config_section.SavePath != null) {
save_path_text_box.Text = config_section.SavePath;
}
Object value = config_section.Delay;
if(value is int) delay_box.Value = (int)value;
}
#else
String dir_path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
dir_path = Path.Combine(dir_path, "rivoreo");
Directory.CreateDirectory(dir_path);
Console.Error.WriteLine("Configuration directory " + dir_path);
config = FlatConfiguration.CreateFromFile(Path.Combine(dir_path, "screen-shooter.cfg"));
String save_path = config.GetString("SavePath", null);
if(save_path != null) save_path_text_box.Text = save_path;
delay_box.Value = config.GetInt32("Delay", 0);
int width = config.GetInt32("WindowWidth", 0);
if(width > 0) Width = width;
int height = config.GetInt32("WindowHeight", 0);
if(height > 0) Height = height;
#endif
}
private void 重新截图(Object o, EventArgs a) {
Size size = Size;
SuspendLayout();
Hide();
Application.DoEvents();
try {
#if HAVE_SYSTEM_CONFIGURATION
config_section.Delay = (int)delay_box.Value;
config.Save(ConfigurationSaveMode.Full);
#else
config.Set("Delay", (int)delay_box.Value);
config.Save();
#endif
} catch(Exception e) {
Console.Error.WriteLine(e);
}
Thread.Sleep((int)delay_box.Value * 1000);
Bitmap bitmap = shoot_screen((ShootTarget)target_box.SelectedIndex);
if(bitmap != null) 预览区域.Image = bitmap;
Show();
Size = size;
ResumeLayout();
}
private void Quit(Object o, EventArgs a) {
清理(null, null);
Application.Exit();
}
internal void Copy(Object o, EventArgs a) {
Clipboard.SetImage(预览区域.Image);
if(Clipboard.GetImage() == null) {
Console.Error.WriteLine("System.Windows.Forms.Clipboard didn't work for image");
X11.CopyImage(预览区域.Image);
has_used_x11_selection = true;
}
}
private void Save(Object o, EventArgs a) {
String path = Path.Combine(save_path_text_box.Text, file_name_text_box.Text);
if(File.Exists(path)) {
MessageBox.Show(path + " already exists", "Failed to save screenshot");
return;
}
ImageFormat format;
String suffix = Path.GetExtension(file_name_text_box.Text);
if(suffix.Equals(".jpeg", StringComparison.CurrentCultureIgnoreCase) ||
suffix.Equals(".jpg", StringComparison.CurrentCultureIgnoreCase)) {
format = ImageFormat.Jpeg;
} else if(suffix.Equals(".bmp", StringComparison.CurrentCultureIgnoreCase)) {
format = ImageFormat.Bmp;
} else if(suffix.Equals(".exif", StringComparison.CurrentCultureIgnoreCase)) {
format = ImageFormat.Exif;
} else if(suffix.Equals(".gif", StringComparison.CurrentCultureIgnoreCase)) {
format = ImageFormat.Gif;
} else if(suffix.Equals(".tiff", StringComparison.CurrentCultureIgnoreCase)) {
format = ImageFormat.Tiff;
} else {
format = ImageFormat.Png;
}
try {
预览区域.Image.Save(path, format);
try {
#if HAVE_SYSTEM_CONFIGURATION
//config_section["SavePath"] = save_path_text_box.Text;
config_section.SavePath = save_path_text_box.Text;
config.Save(ConfigurationSaveMode.Full);
#else
config.Set("SavePath", save_path_text_box.Text);
if(WindowState == FormWindowState.Normal) {
config.Set("WindowWidth", Width);
config.Set("WindowHeight", Height);
}
config.Save();
#endif
} catch(Exception e) {
Console.Error.WriteLine(e);
}
清理(null, null);
Application.Exit();
} catch(Exception e) {
Console.Error.WriteLine(e);
MessageBox.Show(e.ToString(), "Failed to save screenshot");
}
}
private void 清理(Object o, EventArgs a) {
if(has_used_x11_selection && !X11.QuitSelectionOwnerThread(true)) {
/* The selection owner thread has failed to
* stop, and Environment.Exit didn't work
* either, may need calling exit(3) in libc.
*/
}
}
private static TextBox create_flat_read_only_text_box(String text) {
TextBox textbox = new TextBox();
textbox.Anchor = AnchorStyles.Left;
textbox.ReadOnly = true;
textbox.BorderStyle = BorderStyle.None;
textbox.Text = text;
textbox.HideSelection = true;
textbox.TabIndex = Int32.MaxValue;
//textbox.Enter += (o, a) => textbox.SelectionLength = 0;
textbox.Enter += (o, a) => textbox.SelectionStart = 0;
textbox.Width = 90;
return textbox;
}
private static void show_command_line_usage_dialog() {
TableLayoutPanel layout_panel = new TableLayoutPanel();
layout_panel.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
layout_panel.ColumnCount = 2;
layout_panel.RowCount = 5;
layout_panel.ColumnStyles.Add(new ColumnStyle());
layout_panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
/*
Label label0 = new Label();
label0.Text = "Options:";
layout_panel.Controls.Add(label0, 0, 0);
*/
TextBox textbox1 = create_flat_read_only_text_box("--delay <n>");
layout_panel.Controls.Add(textbox1, 0, 0);
Label label1 = new Label();
label1.Dock = DockStyle.Fill;
label1.Anchor = AnchorStyles.Left | AnchorStyles.Right;
label1.Height = textbox1.Height;
label1.Text = "Delay <n> seconds before shooting";
layout_panel.Controls.Add(label1, 1, 0);
TextBox textbox2 = create_flat_read_only_text_box("--active-window");
layout_panel.Controls.Add(textbox2, 0, 1);
Label label2 = new Label();
label2.Anchor = AnchorStyles.Left | AnchorStyles.Right;
label2.Height = textbox2.Height;
label2.Text = "Shoot the active window only";
layout_panel.Controls.Add(label2, 1, 1);
TextBox textbox3 = create_flat_read_only_text_box("--select-region");
layout_panel.Controls.Add(textbox3, 0, 2);
Label label3 = new Label();
label3.Anchor = AnchorStyles.Left | AnchorStyles.Right;
label3.Height = textbox3.Height;
label3.Text = "Take a full screenshot first, then interactively select a region to preserve";
layout_panel.Controls.Add(label3, 1, 2);
TextBox textbox4 = create_flat_read_only_text_box("--copy");
layout_panel.Controls.Add(textbox4, 0, 3);
Label label4 = new Label();
label4.Anchor = AnchorStyles.Left | AnchorStyles.Right;
label4.Height = textbox3.Height;
label4.Text = "Automatically copy the screenshot into clipboard";
layout_panel.Controls.Add(label4, 1, 3);
Form form = new Form();
Button button = new Button();
button.Anchor = AnchorStyles.Bottom;
//button.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
button.Text = "&Close";
button.Click += (o, a) => form.Close();
button.TabIndex = 0;
layout_panel.Controls.Add(button, 0, 4);
layout_panel.SetColumnSpan(button, 2);
//form.Controls.Add(button);
form.AcceptButton = button;
form.CancelButton = button;
form.Controls.Add(layout_panel);
form.Text = "Command Line Usage";
form.AutoScaleMode = AutoScaleMode.Font;
//form.ClientSize = new Size(240, 90);
form.ClientSize = new Size(480, 120);
layout_panel.ClientSize = form.ClientSize - new Size(2, 2);
form.StartPosition = FormStartPosition.CenterScreen;
form.MaximizeBox = false;
form.MinimizeBox = false;
form.ShowDialog();
}
[STAThread]
public static int Main(String[] args) {
int delay = -1;
ShootTarget target = ShootTarget.FULL_SCREEN;
bool auto_copy = false;
for(int i = 0; i < args.Length; i++) {
String a = args[i];
if(a.Length > 1 && a[0] == '-') {
switch(a) {
case "--delay":
if(++i >= args.Length) {
String msg = String.Format("Option '{0}' requires an argument",
a);
Console.Error.WriteLine(msg);
MessageBox.Show(msg, "Error");
return 255;
}
delay = Int32.Parse(args[i]);
break;
case "--active-window":
target = ShootTarget.ACTIVE_WINDOW;
break;
case "--select-region":
target = ShootTarget.SELECT_RECTANGLE;
break;
case "--copy":
auto_copy = true;
break;
case "--help":
show_command_line_usage_dialog();
return 0;
default:
Console.Error.WriteLine("Invalid option " + a);
MessageBox.Show("Invalid option " + a, "Error");
return 255;
}
}
}
if(delay > 0) Thread.Sleep(delay * 1000);
MainForm f = new MainForm(target);
if(delay >= 0) f.delay_box.Value = delay;
if(auto_copy) f.Copy(null, null);
Application.Run(f);
return 0;
}
}
}