diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/crosshair-overlay.iml b/.idea/crosshair-overlay.iml
new file mode 100644
index 0000000..d0876a7
--- /dev/null
+++ b/.idea/crosshair-overlay.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..f07e606
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..038558a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..d73a4a8
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/overlay.iml b/.idea/overlay.iml
new file mode 100644
index 0000000..6690b72
--- /dev/null
+++ b/.idea/overlay.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..a7b7c03
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1658232308929
+
+
+ 1658232308929
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 89cbcfe..1e748f0 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,34 @@
-# crosshair-overlay
-Crosshair overlay
+# 💠Crosshair Overlay
+
+![GitHub](https://img.shields.io/github/license/I-Atlas/crosshair-overlay?style=flat-square)
+![Python](https://img.shields.io/badge/Made%20With-Python%203.10-blue.svg?style=flat-square)
+
+![crosshair](/screenshots/crosshair.PNG)
+
+Crosshair overlay is transparent overlay on screen with crosshair or dot.
+You can adjust the crosshair using the easy-to-use interface.
+There is support for light and dark themes!
+
+
+## 🚀 Start
+
+1. Download overlay.exe from [releases](https://github.com/I-Atlas/crosshair-overlay/releases);
+2. Open overlay.exe;
+3. Click Start button;
+4. Make sure you play the game in a borderless window mode;
+5. Enjoy!
+
+## 🔮 Setting
+
+![menu](/screenshots/menu.PNG)
+
+1. Open or save crosshair settings menu;
+2. Change color of crosshair;
+3. Change the type of crosshair;
+4. Adjustment for crosshair;
+5. Reset settings;
+6. Start or stop overlay crosshair on screen.
+
+## 📑 License
+
+The project is licensed under the [GNU General Public License v3.0](https://github.com/I-Atlas/crosshair-overlay/blob/master/LICENSE).
\ No newline at end of file
diff --git a/fonts/Inter-Regular.otf b/fonts/Inter-Regular.otf
new file mode 100644
index 0000000..fdb121d
Binary files /dev/null and b/fonts/Inter-Regular.otf differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2ce8a45
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,8 @@
+altgraph==0.17.2
+dearpygui==1.6.2
+future==0.18.2
+pefile==2022.5.30
+pyinstaller==5.2
+pyinstaller-hooks-contrib==2022.8
+pywin32-ctypes==0.2.0
+darkdetect~=0.7.1
\ No newline at end of file
diff --git a/screenshots/crosshair.PNG b/screenshots/crosshair.PNG
new file mode 100644
index 0000000..41aa2e4
Binary files /dev/null and b/screenshots/crosshair.PNG differ
diff --git a/screenshots/menu.PNG b/screenshots/menu.PNG
new file mode 100644
index 0000000..1d05db0
Binary files /dev/null and b/screenshots/menu.PNG differ
diff --git a/src/overlay.py b/src/overlay.py
new file mode 100644
index 0000000..97636f9
--- /dev/null
+++ b/src/overlay.py
@@ -0,0 +1,90 @@
+import tkinter as tk
+
+
+class CrosshairOverlay(tk.Tk):
+ def __init__(self, size=4, width=1, distance=2, color_r=255, color_g=255, color_b=255, is_outline=False,
+ is_center_dot=False, is_crosshair=True, offset_x=0, offset_y=0):
+ super().__init__()
+ self.l = size
+ self.w = width
+ self.tn = distance
+ self.nk = self._nk(self.w, self.tn)
+ self.L = self._L(self.l, self.nk)
+ self.LL = self._LL(self.L, self.w)
+ self.ox = self.winfo_screenwidth() / 2 - self.LL + offset_x
+ self.oy = self.winfo_screenheight() / 2 - self.LL + offset_y
+ self.c = "#" + "".join(map(lambda x: format(int(x), "02x"), [color_r, color_g, color_b])).upper()
+ self.iol = is_outline
+ self.icd = is_center_dot
+ self.ich = is_crosshair
+
+ self.geometry(self._gm(self.L + 4, self.L + 4, self.ox, self.oy))
+ self.canvas = tk.Canvas(self, bg="snow", width=self.L + self.w, height=self.L + self.w,
+ highlightthickness=0)
+ self.canvas.pack(fill=tk.BOTH, expand=True)
+
+ self._init_transparent()
+ self._create_crosshair_or_dot()
+
+ def _init_transparent(self):
+ self.overrideredirect(True)
+ self.wm_attributes("-disabled", True)
+ self.wm_attributes("-topmost", True)
+ self.wm_attributes("-transparentcolor", "snow")
+
+ def _create_crosshair_or_dot(self):
+ if self.ich:
+ self._create_crosshair()
+ if self.icd:
+ self._create_center_dot()
+
+ def _create_crosshair(self):
+ self._create_crosshair_up()
+ self._create_crosshair_down()
+ self._create_crosshair_left()
+ self._create_crosshair_right()
+
+ def _create_crosshair_up(self):
+ if self.iol:
+ self.canvas.create_rectangle(self.LL, 0, self.LL + self.w + 1, self.l + 1, fill=self.c)
+ else:
+ self.canvas.create_rectangle(self.LL, 0, self.LL + self.w + 1, self.l + 1, fill=self.c, outline="snow")
+
+ def _create_crosshair_down(self):
+ if self.iol:
+ self.canvas.create_rectangle(self.LL, self.l + self.nk, self.LL + self.w + 1, self.L + 1, fill=self.c)
+ else:
+ self.canvas.create_rectangle(self.LL, self.l + self.nk, self.LL + self.w + 1, self.L + 1, fill=self.c,
+ outline="snow")
+
+ def _create_crosshair_left(self):
+ if self.iol:
+ self.canvas.create_rectangle(0, self.LL, self.l + 1, self.LL + self.w + 1, fill=self.c)
+ else:
+ self.canvas.create_rectangle(0, self.LL, self.l + 1, self.LL + self.w + 1, fill=self.c, outline="snow")
+
+ def _create_crosshair_right(self):
+ if self.iol:
+ self.canvas.create_rectangle(self.l + self.nk, self.LL, self.L + 1, self.LL + self.w + 1, fill=self.c)
+ else:
+ self.canvas.create_rectangle(self.l + self.nk, self.LL, self.L + 1, self.LL + self.w + 1, fill=self.c,
+ outline="snow")
+
+ def _create_center_dot(self):
+ if self.iol:
+ self.canvas.create_rectangle(self.LL, self.LL, self.LL + self.w + 1, self.LL + self.w + 1, fill=self.c)
+ else:
+ self.canvas.create_rectangle(self.LL, self.LL, self.LL + self.w + 1, self.LL + self.w + 1, fill=self.c,
+ outline="snow")
+
+ def _gm(self, ww, wh, offset_x, offset_y):
+ return str(int(ww)) + "x" + str(int(wh)) + "+" + str(int(offset_x)) + "+" + str(int(offset_y))
+
+ def _nk(self, w, tn):
+ return w + tn * 2
+
+ def _L(self, l, nk):
+ return nk + l * 2
+
+ def _LL(self, L, w):
+ return (L - w) / 2
\ No newline at end of file
diff --git a/src/ui.py b/src/ui.py
new file mode 100644
index 0000000..ff55ba7
--- /dev/null
+++ b/src/ui.py
@@ -0,0 +1,153 @@
+import json
+import os.path
+import tkinter as tk
+import multiprocessing
+import dearpygui.dearpygui as dpg
+from dearpygui_ext.themes import create_theme_imgui_light, create_theme_imgui_dark
+import overlay
+import darkdetect
+
+p = multiprocessing.Process()
+
+
+def _get_theme():
+ light_theme = create_theme_imgui_light()
+ dark_theme = create_theme_imgui_dark()
+ if darkdetect.isDark():
+ return dark_theme
+ else:
+ return light_theme
+
+
+def ui_loop(ww=1920, wh=1080):
+ dpg.create_context()
+
+ with dpg.window(tag="Primary Window"):
+ with dpg.menu_bar():
+ with dpg.menu(label="Menu"):
+ with dpg.file_dialog(label="File Open", width=400, height=300, show=False,
+ callback=_open_settings, tag="__file_open"):
+ dpg.add_file_extension(".json", color=(255, 255, 255, 255))
+ dpg.add_menu_item(label="Open", user_data=dpg.last_container(),
+ callback=lambda s, a, u: dpg.configure_item(u, show=True))
+
+ with dpg.file_dialog(label="File Save", width=400, height=300, show=False,
+ callback=_save_settings, tag="__file_save"):
+ dpg.add_file_extension(".json", color=(255, 255, 255, 255))
+ dpg.add_menu_item(label="Save", user_data=dpg.last_container(),
+ callback=lambda s, a, u: dpg.configure_item(u, show=True))
+
+ dpg.add_color_edit((255, 255, 255, 255), label="Color", no_alpha=True, callback=_set_values,
+ tag="color")
+
+ dpg.add_checkbox(label="Crosshair", default_value=True, callback=_set_values, tag="is_crosshair")
+ dpg.add_checkbox(label="Center Dot", callback=_set_values, tag="is_center_dot")
+ dpg.add_checkbox(label="Outline", callback=_set_values, tag="is_outline")
+
+ dpg.add_input_int(label="Size", default_value=4, callback=_set_values, tag="size")
+ dpg.add_input_int(label="Width", default_value=1, callback=_set_values, tag="width")
+ dpg.add_input_int(label="Center distance", default_value=2, callback=_set_values, tag="distance")
+ dpg.add_input_int(label="Offset X", callback=_set_values, min_value=int(-ww / 2),
+ max_value=int(ww / 2),
+ min_clamped=True, max_clamped=True, tag="offset_x")
+ dpg.add_input_int(label="Offset Y", callback=_set_values, min_value=int(-wh / 2),
+ max_value=int(wh / 2),
+ min_clamped=True, max_clamped=True,
+ tag="offset_y")
+
+ with dpg.group(label="Buttons", tag="start_stop_buttons", horizontal=True):
+ dpg.add_button(label="Reset settings", callback=_reset)
+ dpg.add_button(label="Open overlay", callback=_start)
+ dpg.add_button(label="Close overlay", callback=_stop)
+
+ theme = _get_theme()
+ dpg.bind_theme(theme)
+
+ dpg.create_viewport(title='Crosshair Overlay', width=1280, height=720)
+ dpg.setup_dearpygui()
+ dpg.show_viewport()
+ dpg.set_primary_window("Primary Window", True)
+ dpg.start_dearpygui()
+ dpg.destroy_context()
+
+
+def _crosshair_overlay(size=4, width=1, distance=2, color_r=255, color_g=255, color_b=255, is_outline=False,
+ is_center_dot=False, is_crosshair=True, offset_x=0, offset_y=0):
+ overlay.CrosshairOverlay(size=size, width=width, distance=distance, color_r=color_r, color_g=color_g,
+ color_b=color_b,
+ is_outline=is_outline,
+ is_center_dot=is_center_dot, is_crosshair=is_crosshair, offset_x=offset_x,
+ offset_y=offset_y).mainloop()
+
+
+def _set_values():
+ if p.is_alive():
+ _start()
+
+
+def _start():
+ global p
+ if p.is_alive():
+ p.kill()
+ p.join()
+ p = multiprocessing.Process(target=_crosshair_overlay, kwargs=_get_values(), daemon=True)
+ p.start()
+
+
+def _stop():
+ if p.is_alive():
+ p.kill()
+ p.join()
+
+
+def _get_values():
+ r, g, b = list(map(int, dpg.get_value("color")[:3]))
+ return {"size": dpg.get_value("size"), "width": dpg.get_value("width"), "distance": dpg.get_value("distance"),
+ "color_r": r,
+ "color_g": g, "color_b": b, "is_outline": dpg.get_value("is_outline"),
+ "is_center_dot": dpg.get_value("is_center_dot"), "is_crosshair": dpg.get_value("is_crosshair"),
+ "offset_x": dpg.get_value("offset_x"),
+ "offset_y": -dpg.get_value("offset_y")}
+
+
+def _reset():
+ dpg.set_value("color", (255, 255, 255, 255))
+ dpg.set_value("is_outline", False)
+ dpg.set_value("is_center_dot", False)
+ dpg.set_value("is_crosshair", True)
+ dpg.set_value("size", 4)
+ dpg.set_value("width", 1)
+ dpg.set_value("distance", 2)
+ dpg.set_value("offset_x", 0)
+ dpg.set_value("offset_y", 0)
+ _set_values()
+
+
+def _open_settings(sender, app_data):
+ if os.path.isfile(app_data["file_path_name"]):
+ with open(app_data["file_path_name"], "r") as fp:
+ data = json.load(fp)
+ dpg.set_value("color", (data["color_r"], data["color_g"], data["color_b"], 255))
+ dpg.set_value("is_outline", data["is_outline"])
+ dpg.set_value("is_center_dot", data["is_center_dot"])
+ dpg.set_value("is_crosshair", data["is_crosshair"])
+ dpg.set_value("size", data["size"])
+ dpg.set_value("width", data["width"])
+ dpg.set_value("distance", data["distance"])
+ dpg.set_value("offset_x", data["offset_x"])
+ dpg.set_value("offset_y", data["offset_y"])
+ _set_values()
+
+
+def _save_settings(sender, app_data):
+ with open(app_data["file_path_name"], "w") as fp:
+ json.dump(_get_values(), fp)
+
+
+if __name__ == '__main__':
+ multiprocessing.freeze_support()
+ root = tk.Tk()
+ ww = root.winfo_screenwidth()
+ wh = root.winfo_screenheight()
+ root.destroy()
+ ui_loop(ww=ww, wh=wh)