| /* |
| Copyright 2015-2022 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; |
| } |
| |
| 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; |
| //location = f.SelectionLocation; |
| //size = f.SelectionSize; |
| SetDesktopLocation(f.SelectionLocation.X, f.SelectionLocation.Y); |
| 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(target == ShootTarget.FULL_SCREEN || target == ShootTarget.ACTIVE_WINDOW) { |
| f.CenterToScreen(); |
| } |
| if(auto_copy) f.Copy(null, null); |
| Application.Run(f); |
| return 0; |
| } |
| } |
| } |