diff --git a/mesh_snap_utilities_line/__init__.py b/mesh_snap_utilities_line/__init__.py
index 585113396e4581f0844d4231a7f6fe7e31adedda..0ce402b5cc0eea31ce20aa76a8b64e9dee5a8555 100644
--- a/mesh_snap_utilities_line/__init__.py
+++ b/mesh_snap_utilities_line/__init__.py
@@ -22,37 +22,40 @@
 bl_info = {
     "name": "Snap_Utilities_Line",
     "author": "Germano Cavalcante",
-    "version": (5, 8, 30),
+    "version": (5, 9, 00),
     "blender": (2, 80, 0),
-    "location": "View3D > TOOLS > Make Line",
+    "location": "View3D > TOOLS > Line Tool",
     "description": "Extends Blender Snap controls",
     #"wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities",
     "category": "Mesh"}
 
 if "bpy" in locals():
     import importlib
+    importlib.reload(common_classes)
     importlib.reload(preferences)
     importlib.reload(ops_line)
-    importlib.reload(common_classes)
 else:
+    from . import common_classes
     from . import preferences
     from . import ops_line
 
 import bpy
 from bpy.utils.toolsystem import ToolDef
 
+if not __package__:
+    __package__ = "mesh_snap_utilities_line"
+
 @ToolDef.from_fn
-def tool_make_line():
+def tool_line():
     import os
     def draw_settings(context, layout, tool):
-        addon_prefs = context.preferences.addons["mesh_snap_utilities_line"].preferences
+        addon_prefs = context.preferences.addons[__package__].preferences
 
         layout.prop(addon_prefs, "incremental")
         layout.prop(addon_prefs, "increments_grid")
-        if addon_prefs.increments_grid:
-            layout.prop(addon_prefs, "relative_scale")
         layout.prop(addon_prefs, "create_face")
-        layout.prop(addon_prefs, "outer_verts")
+        if context.mode == 'EDIT_MESH':
+            layout.prop(addon_prefs, "outer_verts")
         #props = tool.operator_properties("mesh.snap_utilities_line")
         #layout.prop(props, "radius")
 
@@ -64,9 +67,9 @@ def tool_make_line():
             "Make Lines\n"
             "Connect them to split faces"
         ),
-        icon=os.path.join(icons_dir, "ops.mesh.make_line"),
-        #widget="MESH_GGT_mouse_point",
-        operator="mesh.make_line",
+        icon=os.path.join(icons_dir, "ops.mesh.snap_utilities_line"),
+        widget="MESH_GGT_snap_point",
+        #operator="mesh.snap_utilities_line",
         keymap="3D View Tool: Edit Mesh, Make Line",
         draw_settings=draw_settings,
     )
@@ -75,12 +78,13 @@ def tool_make_line():
 # -----------------------------------------------------------------------------
 # Tool Registraion
 
-def km_3d_view_tool_make_line(tool_mouse = 'LEFTMOUSE'):
+def km_3d_view_snap_tools(tool_mouse = 'LEFTMOUSE'):
     return [(
-        "3D View Tool: Edit Mesh, Make Line",
+        tool_line.keymap[0],
         {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
         {"items": [
-            ("mesh.make_line", {"type": tool_mouse, "value": 'CLICK'}, None),
+            ("mesh.snap_utilities_line", {"type": tool_mouse, "value": 'CLICK'},
+             {"properties": [("wait_for_input", False)]}),
         ]},
     )]
 
@@ -91,67 +95,75 @@ def get_tool_list(space_type, context_mode):
     return cls._tools[context_mode]
 
 
-def register_make_line_tool():
+def register_snap_tools():
     tools = get_tool_list('VIEW_3D', 'EDIT_MESH')
 
     for index, tool in enumerate(tools, 1):
-        if isinstance(tool, ToolDef) and tool.text == "Add Cube":
+        if isinstance(tool, ToolDef) and tool.text == "Measure":
             break
 
-    tools.insert(index, tool_make_line)
+    tools[:index] += None, tool_line
+
+    del tools
 
     keyconfigs = bpy.context.window_manager.keyconfigs
     kc_defaultconf = keyconfigs.get("blender")
     kc_addonconf   = keyconfigs.get("blender addon")
 
-    # TODO: find the user defined tool_mouse.
-    keyconfig_data = km_3d_view_tool_make_line()
+    keyconfig_data = km_3d_view_snap_tools()
 
+    # TODO: find the user defined tool_mouse.
     from bl_keymap_utils.io import keyconfig_init_from_data
     keyconfig_init_from_data(kc_defaultconf, keyconfig_data)
     keyconfig_init_from_data(kc_addonconf, keyconfig_data)
 
 
-def unregister_make_line_tool():
+def unregister_snap_tools():
     tools = get_tool_list('VIEW_3D', 'EDIT_MESH')
-    tools.remove(tool_make_line)
 
-    km_name, km_args, km_content = km_3d_view_tool_make_line()[0]
+    index = tools.index(tool_line) - 1 #None
+    tools.pop(index)
+    tools.remove(tool_line)
+
+    del tools
+    del index
 
     keyconfigs = bpy.context.window_manager.keyconfigs
     defaultmap = keyconfigs.get("blender").keymaps
     addonmap   = keyconfigs.get("blender addon").keymaps
 
-    addonmap.remove(addonmap.find(km_name, **km_args))
-    defaultmap.remove(defaultmap.find(km_name, **km_args))
+    for keyconfig_data in km_3d_view_snap_tools():
+        km_name, km_args, km_content = keyconfig_data
+
+        addonmap.remove(addonmap.find(km_name, **km_args))
+        defaultmap.remove(defaultmap.find(km_name, **km_args))
 
 
 # -----------------------------------------------------------------------------
 # Addon Registraion
 
+classes = (
+    preferences.SnapUtilitiesLinePreferences,
+    ops_line.SnapUtilitiesLine,
+    common_classes.VIEW3D_OT_rotate_custom_pivot,
+    common_classes.VIEW3D_OT_zoom_custom_target,
+    common_classes.SnapPointWidget,
+    common_classes.SnapPointWidgetGroup,
+)
+
 def register():
-    bpy.utils.register_class(preferences.SnapUtilitiesLinePreferences)
-    bpy.utils.register_class(common_classes.VIEW3D_OT_rotate_custom_pivot)
-    bpy.utils.register_class(common_classes.VIEW3D_OT_zoom_custom_target)
-    bpy.utils.register_class(ops_line.SnapUtilitiesLine)
-    #bpy.utils.register_class(common_classes.MousePointWidget)
-    #bpy.utils.register_class(common_classes.MousePointWidgetGroup)
+    for cls in classes:
+        bpy.utils.register_class(cls)
 
-    register_make_line_tool()
+    register_snap_tools()
 
 
 def unregister():
-    unregister_make_line_tool()
+    unregister_snap_tools()
 
-    #bpy.utils.unregister_class(common_classes.MousePointWidgetGroup)
-    #bpy.utils.unregister_class(common_classes.MousePointWidget)
-    bpy.utils.unregister_class(ops_line.SnapUtilitiesLine)
-    bpy.utils.unregister_class(common_classes.VIEW3D_OT_zoom_custom_target)
-    bpy.utils.unregister_class(common_classes.VIEW3D_OT_rotate_custom_pivot)
-    bpy.utils.unregister_class(preferences.SnapUtilitiesLinePreferences)
+    for cls in reversed(classes):
+        bpy.utils.unregister_class(cls)
 
 
 if __name__ == "__main__":
-    __name__ = "mesh_snap_utilities_line"
-    __package__ = "mesh_snap_utilities_line"
     register()
diff --git a/mesh_snap_utilities_line/common_classes.py b/mesh_snap_utilities_line/common_classes.py
index 1c6d75db05bf026f4601480ff3fa4a67ae496169..ae763828c0ad5e46cd68e04c8c1b782bf52e64f3 100644
--- a/mesh_snap_utilities_line/common_classes.py
+++ b/mesh_snap_utilities_line/common_classes.py
@@ -18,7 +18,13 @@
 import bpy
 import bgl
 
-from .common_utilities import snap_utilities
+from mathutils import Vector
+
+from .common_utilities import (
+    convert_distance,
+    get_units_info,
+    snap_utilities,
+    )
 
 
 class SnapDrawn():
@@ -52,8 +58,6 @@ class SnapDrawn():
         self._program_smooth_col = gpu.shader.from_builtin("3D_SMOOTH_COLOR")
 
         self._batch_point = None
-        self._batch_circle = None
-        self._batch_vector = None
 
 
     def batch_line_strip_create(self, coords):
@@ -171,6 +175,7 @@ class SnapDrawn():
         gpu.matrix.pop()
 
     def draw_elem(self, snap_obj, bm, elem):
+        #TODO: Cache coords (because antialiasing)
         import gpu
         from bmesh.types import(
             BMVert,
@@ -332,122 +337,67 @@ class CharMap:
         'LEFT_ARROW', 'RIGHT_ARROW'
         }
 
-    @staticmethod
-    def modal(self, context, event):
-        c = event.ascii
-        if c:
-            if c == ",":
-                c = "."
-            self.length_entered = self.length_entered[:self.line_pos] + c + self.length_entered[self.line_pos:]
-            self.line_pos += 1
-        if self.length_entered:
-            if event.type == 'BACK_SPACE':
-                self.length_entered = self.length_entered[:self.line_pos - 1] + self.length_entered[self.line_pos:]
-                self.line_pos -= 1
-
-            elif event.type == 'DEL':
-                self.length_entered = self.length_entered[:self.line_pos] + self.length_entered[self.line_pos + 1:]
-
-            elif event.type == 'LEFT_ARROW':
-                self.line_pos = (self.line_pos - 1) % (len(self.length_entered) + 1)
-
-            elif event.type == 'RIGHT_ARROW':
-                self.line_pos = (self.line_pos + 1) % (len(self.length_entered) + 1)
-
-g_snap_widget = [None]
-
-class MousePointWidget(bpy.types.Gizmo):
-    bl_idname = "VIEW3D_GT_mouse_point"
+    def __init__(self, context):
+        scale = context.scene.unit_settings.scale_length
+        separate_units = context.scene.unit_settings.use_separate
+        self.unit_system = context.scene.unit_settings.system
+        self.uinfo = get_units_info(scale, self.unit_system, separate_units)
+
+        self.clear()
+
+    def modal_(self, context, event):
+        if event.value == 'PRESS':
+            type = event.type
+            ascii = event.ascii
+            if (type in self.type) or (ascii in self.ascii):
+                if ascii:
+                    pos = self.line_pos
+                    if ascii == ",":
+                        ascii = "."
+                    self.length_entered = self.length_entered[:pos] + ascii + self.length_entered[pos:]
+                    self.line_pos += 1
+
+                if self.length_entered:
+                    pos = self.line_pos
+                    if type == 'BACK_SPACE':
+                        self.length_entered = self.length_entered[:pos - 1] + self.length_entered[pos:]
+                        self.line_pos -= 1
+
+                    elif type == 'DEL':
+                        self.length_entered = self.length_entered[:pos] + self.length_entered[pos + 1:]
+
+                    elif type == 'LEFT_ARROW':
+                        self.line_pos = (pos - 1) % (len(self.length_entered) + 1)
+
+                    elif type == 'RIGHT_ARROW':
+                        self.line_pos = (pos + 1) % (len(self.length_entered) + 1)
+
+                    try:
+                        self.length_entered_value = bpy.utils.units.to_value(
+                                self.unit_system, 'LENGTH', self.length_entered)
+                    except:  # ValueError:
+                        self.length_entered_value = 0.0 #invalid
+                        #self.report({'INFO'}, "Operation not supported yet")
+                else:
+                    self.length_entered_value = 0.0
 
-    __slots__ = (
-        "sctx",
-        "bm",
-        "draw_cache",
-        "geom",
-        "incremental",
-        "preferences",
-        "loc",
-        "snap_obj",
-        "snap_to_grid",
-        "type",
-    )
+                return True
 
-    def test_select(self, context, mval):
-        #print('test_select', mval)
-        self.snap_obj, prev_loc, self.loc, self.type, self.bm, self.geom, len = snap_utilities(
-                self.sctx,
-                None,
-                mval,
-                increment=self.incremental
-        )
-        context.area.tag_redraw()
         return False
 
-    def draw(self, context):
-        if self.bm:
-            self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
-        self.draw_cache.draw(self.type, self.loc, None, None, None)
-
-    def setup(self):
-        if not hasattr(self, "sctx"):
-            global g_snap_widget
-            g_snap_widget[0] = self
-
-            context = bpy.context
-
-            self.preferences = preferences = context.preferences.addons[__package__].preferences
-
-            #Configure the unit of measure
-            self.snap_to_grid = preferences.increments_grid
-            self.incremental = bpy.utils.units.to_value(
-                    context.scene.unit_settings.system, 'LENGTH', str(preferences.incremental))
-
-            self.draw_cache = SnapDrawn(
-                preferences.out_color,
-                preferences.face_color,
-                preferences.edge_color,
-                preferences.vert_color,
-                preferences.center_color,
-                preferences.perpendicular_color,
-                preferences.constrain_shift_color,
-                (*context.preferences.themes[0].user_interface.axis_x, 1.0),
-                (*context.preferences.themes[0].user_interface.axis_y, 1.0),
-                (*context.preferences.themes[0].user_interface.axis_z, 1.0)
-            )
-
-            #Init Snap Context
-            from .snap_context_l import SnapContext
-            from mathutils import Vector
-
-            self.sctx = SnapContext(context.region, context.space_data)
-            self.sctx.set_pixel_dist(12)
-            self.sctx.use_clip_planes(True)
-
-            if preferences.outer_verts:
-                for base in context.visible_bases:
-                    self.sctx.add_obj(base.object, base.object.matrix_world)
-
-            self.sctx.set_snap_mode(True, True, True)
-            self.bm = None
-            self.type = 'OUT'
-            self.loc = Vector()
-
-    def __del__(self):
-        global g_snap_widget
-        g_snap_widget[0] = None
-
+    def get_converted_length_str(self, length):
+        if self.length_entered:
+            pos = self.line_pos
+            ret = self.length_entered[:pos] + '|' + self.length_entered[pos:]
+        else:
+            ret = convert_distance(length, self.uinfo)
 
-class MousePointWidgetGroup(bpy.types.GizmoGroup):
-    bl_idname = "MESH_GGT_mouse_point"
-    bl_label = "Draw Mouse Point"
-    bl_space_type = 'VIEW_3D'
-    bl_region_type = 'WINDOW'
-    bl_options = {'3D'}
+        return ret
 
-    def setup(self, context):
-        snap_widget = self.gizmos.new(MousePointWidget.bl_idname)
-        props = snap_widget.target_set_operator("mesh.make_line")
-        props.wait_for_input = False
+    def clear(self):
+        self.length_entered = ''
+        self.length_entered_value = 0.0
+        self.line_pos = 0
 
 
 class VIEW3D_OT_rotate_custom_pivot(bpy.types.Operator):
@@ -532,3 +482,292 @@ class VIEW3D_OT_zoom_custom_target(bpy.types.Operator):
                 return {'RUNNING_MODAL'}
 
         return {'FINISHED'}
+
+
+class SnapUtilities:
+#    __slots__ = (
+#        "sctx",
+#        "draw_cache",
+#        "outer_verts",
+#        "snap_face",
+#        "snap_to_grid",
+#        "unit_system",
+#        "rd",
+#        "incremental",
+#    )
+
+    constrain_keys = {
+        'X': Vector((1,0,0)),
+        'Y': Vector((0,1,0)),
+        'Z': Vector((0,0,1)),
+        'RIGHT_SHIFT': 'shift',
+        'LEFT_SHIFT': 'shift',
+        }
+
+    snap_widget = None
+    snap_widget_refcnt = 0
+    constrain = None
+
+    @staticmethod
+    def set_contrain(context, key):
+        widget = SnapUtilities.snap_widget
+        if SnapUtilities.constrain == key:
+            SnapUtilities.constrain = None
+            return
+
+        SnapUtilities.constrain = key
+
+
+    def visible_objects_and_duplis(self, context):
+        if self.preferences.outer_verts:
+            for obj in context.visible_objects:
+                yield (obj, obj.matrix_world)
+
+                if obj.instance_type == 'COLLECTION':
+                    mat = obj.matrix_world.copy()
+                    for ob in obj.instance_collection.objects:
+                        yield (ob, mat @ ob.matrix_world)
+        else:
+            for obj in context.objects_in_mode_unique_data:
+                yield (obj, obj.matrix_world)
+
+
+    def snap_context_init(self, context, snap_edge_and_vert = True):
+        from .snap_context_l import global_snap_context_get
+
+        #Create Snap Context
+        self.sctx = global_snap_context_get(context.region, context.space_data)
+        self.sctx.set_pixel_dist(12)
+        self.sctx.use_clip_planes(True)
+
+        widget = self.snap_widget
+
+        if widget is not None:
+            self.preferences = widget.preferences
+            self.draw_cache = widget.draw_cache
+        else:
+            preferences = context.preferences.addons[__package__].preferences
+            self.preferences = preferences
+            #Init DrawCache
+            self.draw_cache = SnapDrawn(
+                preferences.out_color,
+                preferences.face_color,
+                preferences.edge_color,
+                preferences.vert_color,
+                preferences.center_color,
+                preferences.perpendicular_color,
+                preferences.constrain_shift_color,
+                tuple(context.preferences.themes[0].user_interface.axis_x) + (1.0,),
+                tuple(context.preferences.themes[0].user_interface.axis_y) + (1.0,),
+                tuple(context.preferences.themes[0].user_interface.axis_z) + (1.0,)
+            )
+
+        self.snap_vert = self.snap_edge = snap_edge_and_vert
+
+        shading = context.space_data.shading
+        self.snap_face = not (snap_edge_and_vert and
+                             (shading.show_xray or shading.type == 'WIREFRAME'))
+
+        self.snap_context_update(context)
+
+        #Configure the unit of measure
+        unit_system = context.scene.unit_settings.system
+        scale = context.scene.unit_settings.scale_length
+        scale /= context.space_data.overlay.grid_scale
+        self.rd = bpy.utils.units.to_value(unit_system, 'LENGTH', str(1 / scale))
+
+        self.incremental = bpy.utils.units.to_value(
+                unit_system, 'LENGTH', str(self.preferences.incremental))
+
+    def snap_context_update(self, context):
+        self.sctx.set_snap_mode(
+                 self.snap_vert, self.snap_edge, self.snap_face)
+
+        self.sctx.clear_snap_objects()
+
+        for obj, matrix in self.visible_objects_and_duplis(context):
+            self.sctx.add_obj(obj, matrix)
+
+        widget = self.snap_widget
+
+        if widget:
+            self.snap_obj = widget.snap_obj
+            self.bm = widget.bm
+            self.geom = widget.geom
+            self.type = widget.type
+            self.location = widget.location
+        else:
+            #init these variables to avoid errors
+            self.snap_obj = None
+            self.bm = None
+            self.geom = None
+            self.type = 'OUT'
+            self.location = Vector()
+
+    def snap_to_grid(self):
+        if self.type == 'OUT' and self.preferences.increments_grid:
+            loc = self.location / self.rd
+            self.location = Vector((round(loc.x),
+                                    round(loc.y),
+                                    round(loc.z))) * self.rd
+
+    def snap_context_free(self):
+        del self.sctx
+
+        del self.bm
+        del self.draw_cache
+        del self.geom
+        del self.location
+        del self.rd
+        del self.snap_face
+        del self.snap_obj
+        del self.type
+
+        del self.preferences
+
+        SnapUtilities.constrain = None
+
+
+#def mesh_runtime_batchcache_isdirty(me):
+#    import ctypes
+#    batch_cache = ctypes.c_void_p.from_address(me.as_pointer() + 1440)
+#    if batch_cache:
+#        return ctypes.c_bool.from_address(batch_cache.value + 549).value
+#    return False
+
+
+class SnapWidgetCommon:
+    def draw_point_and_elem(self):
+        if self.bm:
+            if self.bm.is_valid and self.geom.is_valid:
+                self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
+            else:
+                self.bm = None
+                self.geom = None
+                self.sctx.update_all()
+
+        self.draw_cache.draw(self.type, self.location, None, None, None)
+
+    def init_snap_widget(self, context, snap_edge_and_vert = True):
+        self.snap_context_init(context, snap_edge_and_vert)
+        self.mode = context.mode
+        self.wm_operators = context.window_manager.operators
+        self.last_operator = self.wm_operators[-1] if self.wm_operators else None
+        self.last_mval = None
+
+    def update_snap(self, context, mval):
+        if self.last_mval == mval:
+            return -1
+        else:
+            self.last_mval = mval
+
+        last_operator = self.wm_operators[-1] if self.wm_operators else None
+        if last_operator != self.last_operator:
+            if (not last_operator or
+                last_operator.name not in {'Select', 'Loop Select', '(De)select All'}):
+                    ## Something has changed since the last time.
+                    # Has the mesh been changed?
+                    # In the doubt lets clear the snap context.
+                    self.sctx.update_all()
+
+            self.last_operator = last_operator
+
+        #print('test_select', mval)
+        space = context.space_data
+        self.sctx.update_viewport_context(context.region, space)
+
+        shading = space.shading
+        snap_face = not ((self.snap_vert or self.snap_edge) and
+                        (shading.show_xray or shading.type == 'WIREFRAME'))
+
+        if snap_face != self.snap_face:
+            self.snap_face = snap_face
+            self.sctx.set_snap_mode(
+                     self.snap_vert, self.snap_edge, self.snap_face)
+
+        self.snap_obj, prev_loc, self.location, self.type, self.bm, self.geom, len = snap_utilities(
+                self.sctx,
+                None,
+                mval,
+                increment=self.incremental
+        )
+
+    def __del__(self):
+        from .snap_context_l import global_snap_context_get
+        sctx = global_snap_context_get(None, None)
+        if sctx:
+            sctx.clear_snap_objects()
+
+
+class SnapPointWidget(SnapUtilities, SnapWidgetCommon, bpy.types.Gizmo):
+    bl_idname = "VIEW3D_GT_snap_point"
+
+    __slots__ = (
+        "bm",
+        "draw_cache",
+        "geom",
+        "incremental",
+        "preferences",
+        "last_operator",
+        "location",
+        "mode",
+        "snap_edge",
+        "snap_face",
+        "snap_vert",
+        "snap_obj",
+        "type",
+        "wm_operators",
+    )
+
+    def test_select(self, context, mval):
+        self.update_snap(context, mval)
+        self.snap_to_grid()
+
+        context.area.tag_redraw()
+        return -1
+
+    def draw(self, context):
+        self.draw_point_and_elem()
+
+    def setup(self):
+        self.init_snap_widget(bpy.context)
+        SnapUtilities.snap_widget = self
+
+
+def context_mode_check(context, widget_group):
+    workspace = context.workspace
+    mode = workspace.tools_mode
+    for tool in workspace.tools:
+        if (tool.widget == widget_group) and (tool.mode == mode):
+            break
+    else:
+        return False
+    return True
+
+class SnapWidgetCommon:
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'WINDOW'
+    bl_options = {'3D'}
+
+    @classmethod
+    def poll(cls, context):
+        return context_mode_check(context, cls.bl_idname)
+#        return context_mode_change(
+#                context, SnapUtilities.snap_widget, cls.bl_idname)
+
+    def init_tool(self, context, gizmo_name):
+        self.gizmos.new(gizmo_name)
+        SnapUtilities.snap_widget_refcnt += 1
+
+    def __del__(self):
+        SnapUtilities.snap_widget_refcnt -= 1
+        if SnapUtilities.snap_widget_refcnt == 0:
+            SnapUtilities.snap_widget = None
+
+
+class SnapPointWidgetGroup(SnapWidgetCommon, bpy.types.GizmoGroup):
+    bl_idname = "MESH_GGT_snap_point"
+    bl_label = "Draw Snap Point"
+
+    def setup(self, context):
+        self.init_tool(context, SnapPointWidget.bl_idname)
diff --git a/mesh_snap_utilities_line/icons/ops.mesh.make_line.dat b/mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat
similarity index 80%
rename from mesh_snap_utilities_line/icons/ops.mesh.make_line.dat
rename to mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat
index fa738db992d5896349a19ece83e62b15e32eac5f..b6706ff5f46467c0c5d39e9069d209ee607be70a 100644
Binary files a/mesh_snap_utilities_line/icons/ops.mesh.make_line.dat and b/mesh_snap_utilities_line/icons/ops.mesh.snap_utilities_line.dat differ
diff --git a/mesh_snap_utilities_line/ops_line.py b/mesh_snap_utilities_line/ops_line.py
index 69be59a700cec9a11702c7ce616fc84e9331e2ba..ba5b5a1adc16256491212901757c1a9a26e3e984 100644
--- a/mesh_snap_utilities_line/ops_line.py
+++ b/mesh_snap_utilities_line/ops_line.py
@@ -27,17 +27,15 @@ from .common_classes import (
     SnapDrawn,
     CharMap,
     SnapNavigation,
-    g_snap_widget, #TODO: remove
+    SnapUtilities,
     )
 
 from .common_utilities import (
-    get_units_info,
-    convert_distance,
     snap_utilities,
     )
 
 if not __package__:
-    __package__ = "mesh_snap_utilities"
+    __package__ = "mesh_snap_utilities_line"
 
 
 def get_closest_edge(bm, point, dist):
@@ -79,7 +77,7 @@ def get_loose_linked_edges(bmvert):
     return linked
 
 
-def draw_line(self, bm_geom, location):
+def make_line(self, bm_geom, location):
     obj = self.main_snap_obj.data[0]
     bm = self.main_bm
     split_faces = set()
@@ -113,8 +111,9 @@ def draw_line(self, bm_geom, location):
                 edge, vert = bmesh.utils.edge_split(bm_geom, bm_geom.verts[0], ret[1])
                 update_edit_mesh = True
 
-            self.list_verts.append(vert)
-            self.geom = vert # hack to highlight in the drawing
+            if self.list_verts == [] or self.list_verts[-1] != vert:
+                self.list_verts.append(vert)
+                self.geom = vert # hack to highlight in the drawing
             # self.list_edges.append(edge)
 
         else:  # constrain point is near
@@ -134,7 +133,6 @@ def draw_line(self, bm_geom, location):
         edge = bm.edges.get([v1, v2])
         if edge:
                 self.list_edges.append(edge)
-
         else:
             if not v2.link_edges:
                 edge = bm.edges.new([v1, v2])
@@ -206,71 +204,31 @@ def draw_line(self, bm_geom, location):
     return [obj.matrix_world @ v.co for v in self.list_verts]
 
 
-class SnapUtilitiesLine(bpy.types.Operator):
+class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
     """Make Lines. Connect them to split faces"""
-    bl_idname = "mesh.make_line"
+    bl_idname = "mesh.snap_utilities_line"
     bl_label = "Line Tool"
     bl_options = {'REGISTER'}
 
     wait_for_input: bpy.props.BoolProperty(name="Wait for Input", default=True)
 
-    constrain_keys = {
-        'X': Vector((1,0,0)),
-        'Y': Vector((0,1,0)),
-        'Z': Vector((0,0,1)),
-        'RIGHT_SHIFT': 'shift',
-        'LEFT_SHIFT': 'shift',
-    }
-
     def _exit(self, context):
-        del self.main_bm #avoids unpredictable crashs
-        del self.bm
+        #avoids unpredictable crashs
+        del self.main_snap_obj
+        del self.main_bm
         del self.list_edges
         del self.list_verts
         del self.list_verts_co
 
         bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
         context.area.header_text_set(None)
-
-        if not self.snap_widget:
-            self.sctx.free()
-            del self.draw_cache
-        else:
-            self.sctx = None
-            self.draw_cache = None
+        self.snap_context_free()
 
         #Restore initial state
         context.tool_settings.mesh_select_mode = self.select_mode
         context.space_data.overlay.show_face_center = self.show_face_center
 
-    def _init_snap_context(self, context):
-        if self.sctx:
-            self.sctx.clear_snap_objects()
-        else:
-            #Create Snap Context
-            from .snap_context_l import SnapContext
-            self.sctx = SnapContext(context.region, context.space_data)
-            self.sctx.set_pixel_dist(12)
-            self.sctx.use_clip_planes(True)
-
-        if self.outer_verts:
-            for base in context.visible_bases:
-                self.sctx.add_obj(base.object, base.object.matrix_world)
-
-        self.sctx.set_snap_mode(True, True, self.snap_face)
-
-        if self.snap_widget:
-            self.geom = self.snap_widget.geom
-            self.type = self.snap_widget.type
-            self.location = self.snap_widget.loc
-            if self.snap_widget.snap_obj:
-                context.view_layer.objects.active = self.snap_widget.snap_obj.data[0]
-        else:
-            #init these variables to avoid errors
-            self.geom = None
-            self.type = 'OUT'
-            self.location = Vector()
-
+    def _init_snap_line_context(self, context):
         self.prevloc = Vector()
         self.list_verts = []
         self.list_edges = []
@@ -278,9 +236,6 @@ class SnapUtilitiesLine(bpy.types.Operator):
         self.bool_update = False
         self.vector_constrain = ()
         self.len = 0
-        self.length_entered = ""
-        self.length_entered_value = 0.0
-        self.line_pos = 0
 
         active_object = context.active_object
         mesh = active_object.data
@@ -295,22 +250,29 @@ class SnapUtilitiesLine(bpy.types.Operator):
         context.area.tag_redraw()
 
         if event.ctrl and event.type == 'Z' and event.value == 'PRESS':
-            del self.bm
-            del self.main_bm
-
             bpy.ops.ed.undo()
-            bpy.ops.object.mode_set(mode='EDIT') # just to be sure
-            bpy.ops.mesh.select_all(action='DESELECT')
-            context.tool_settings.mesh_select_mode = (True, False, True)
-            context.space_data.overlay.show_face_center = True
+            if not self.wait_for_input:
+                self._exit(context)
+                return {'FINISHED'}
+            else:
+                del self.bm
+                del self.main_bm
+                self.charmap.clear()
 
-            self._init_snap_context(context)
-            self.sctx.update_all()
-            return {'RUNNING_MODAL'}
+                bpy.ops.object.mode_set(mode='EDIT') # just to be sure
+                bpy.ops.mesh.select_all(action='DESELECT')
+                context.tool_settings.mesh_select_mode = (True, False, True)
+                context.space_data.overlay.show_face_center = True
+
+                self.snap_context_update(context)
+                self._init_snap_line_context(context)
+                self.sctx.update_all()
+
+                return {'RUNNING_MODAL'}
 
         is_making_lines = bool(self.list_verts_co)
 
-        if (event.type == 'MOUSEMOVE' or self.bool_update) and self.length_entered_value == 0.0:
+        if (event.type == 'MOUSEMOVE' or self.bool_update) and self.charmap.length_entered_value == 0.0:
             if self.rv3d.view_matrix != self.rotMat:
                 self.rotMat = self.rv3d.view_matrix.copy()
                 self.bool_update = True
@@ -326,14 +288,9 @@ class SnapUtilitiesLine(bpy.types.Operator):
                     mval,
                     constrain=self.vector_constrain,
                     previous_vert=(self.list_verts[-1] if self.list_verts else None),
-                    increment=self.incremental
-            )
+                    increment=self.incremental)
 
-            if self.snap_to_grid and self.type == 'OUT':
-                loc = self.location / self.rd
-                self.location = Vector((round(loc.x),
-                                        round(loc.y),
-                                        round(loc.z))) * self.rd
+            self.snap_to_grid()
 
             if is_making_lines and self.keyf8:
                 lloc = self.list_verts_co[-1]
@@ -356,24 +313,14 @@ class SnapUtilitiesLine(bpy.types.Operator):
                 #del view_vec, orig, location, ax, ay, az, vec
 
         if event.value == 'PRESS':
-            if is_making_lines and (event.ascii in CharMap.ascii or event.type in CharMap.type):
-                CharMap.modal(self, context, event)
-
-                if self.length_entered != "":
-                    try:
-                        self.length_entered_value = bpy.utils.units.to_value(
-                                self.unit_system, 'LENGTH', self.length_entered)
+            if is_making_lines and self.charmap.modal_(context, event):
+                self.bool_update = self.charmap.length_entered_value == 0.0
 
-                        vector = (self.location - self.list_verts_co[-1]).normalized()
-                        self.location = (self.list_verts_co[-1] + (vector * self.length_entered_value))
-                        del vector
-
-                    except:  # ValueError:
-                        self.length_entered_value = 0.0 #invalid
-                        self.report({'INFO'}, "Operation not supported yet")
-                else:
-                    self.length_entered_value = 0.0
-                    self.bool_update = True
+                if not self.bool_update:
+                    text_value = self.charmap.length_entered_value
+                    vector = (self.location - self.list_verts_co[-1]).normalized()
+                    self.location = self.list_verts_co[-1] + (vector * text_value)
+                    del vector
 
             elif event.type in self.constrain_keys:
                 self.bool_update = True
@@ -400,7 +347,7 @@ class SnapUtilitiesLine(bpy.types.Operator):
                         self.vector_constrain = [loc, loc + self.constrain_keys[event.type], event.type]
 
             elif event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}:
-                if event.type == 'LEFTMOUSE' or self.length_entered_value != 0.0:
+                if event.type == 'LEFTMOUSE' or self.charmap.length_entered_value != 0.0:
                     if not is_making_lines and self.bm:
                         self.main_snap_obj = self.snap_obj
                         self.main_bm = self.bm
@@ -415,12 +362,10 @@ class SnapUtilitiesLine(bpy.types.Operator):
                     if self.vector_constrain:
                         geom2 = get_closest_edge(self.main_bm, point, .001)
 
-                    self.list_verts_co = draw_line(self, geom2, point)
+                    self.list_verts_co = make_line(self, geom2, point)
 
                     self.vector_constrain = None
-                    self.length_entered = ""
-                    self.length_entered_value = 0.0
-                    self.line_pos = 0
+                    self.charmap.clear()
                 else:
                     self._exit(context)
                     return {'FINISHED'}
@@ -441,19 +386,11 @@ class SnapUtilitiesLine(bpy.types.Operator):
                     self.list_edges = []
                     self.list_verts = []
                     self.list_verts_co = []
-                    self.length_entered = ""
-                    self.length_entered_value = 0.0
-                    self.line_pos = 0
+                    self.charmap.clear()
 
         a = ""
         if is_making_lines:
-            if self.length_entered:
-                pos = self.line_pos
-                a = 'length: ' + self.length_entered[:pos] + '|' + self.length_entered[pos:]
-            else:
-                length = self.len
-                length = convert_distance(length, self.uinfo)
-                a = 'length: ' + length
+            a = 'length: ' + self.charmap.get_converted_length_str(self.len)
 
         context.area.header_text_set(text = "hit: %.3f %.3f %.3f %s" % (*self.location, a))
 
@@ -469,6 +406,14 @@ class SnapUtilitiesLine(bpy.types.Operator):
 
     def invoke(self, context, event):
         if context.space_data.type == 'VIEW_3D':
+            self.snap_context_init(context)
+            self.intersect = self.preferences.intersect
+            self.create_face = self.preferences.create_face
+            self.navigation_ops = SnapNavigation(context, True)
+            self.charmap = CharMap(context)
+
+            self._init_snap_line_context(context)
+
             # print('name', __name__, __package__)
 
             #Store current state
@@ -487,57 +432,14 @@ class SnapUtilitiesLine(bpy.types.Operator):
 
             #Init event variables
             self.keyf8 = False
-            self.snap_face = True
-
-            self.snap_widget = g_snap_widget[0]
-
-            if self.snap_widget is not None:
-                self.draw_cache = self.snap_widget.draw_cache
-                self.sctx = self.snap_widget.sctx
-
-                preferences = self.snap_widget.preferences
-            else:
-                preferences = context.preferences.addons[__package__].preferences
-
-                #Init DrawCache
-                self.draw_cache = SnapDrawn(
-                    preferences.out_color,
-                    preferences.face_color,
-                    preferences.edge_color,
-                    preferences.vert_color,
-                    preferences.center_color,
-                    preferences.perpendicular_color,
-                    preferences.constrain_shift_color,
-                    tuple(context.preferences.themes[0].user_interface.axis_x) + (1.0,),
-                    tuple(context.preferences.themes[0].user_interface.axis_y) + (1.0,),
-                    tuple(context.preferences.themes[0].user_interface.axis_z) + (1.0,)
-                )
-
-                self.sctx = None
-
-            #Configure the unit of measure
-            self.unit_system = context.scene.unit_settings.system
-            scale = context.scene.unit_settings.scale_length
-            separate_units = context.scene.unit_settings.use_separate
-            self.uinfo = get_units_info(scale, self.unit_system, separate_units)
-            scale /= context.space_data.overlay.grid_scale * preferences.relative_scale
-            self.rd = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(1 / scale))
-
-            self.intersect = preferences.intersect
-            self.create_face = preferences.create_face
-            self.outer_verts = preferences.outer_verts
-            self.snap_to_grid = preferences.increments_grid
-            self.incremental = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(preferences.incremental))
-
-            self.navigation_ops = SnapNavigation(context, True)
-
-            self._init_snap_context(context)
 
             #modals
             context.window_manager.modal_handler_add(self)
 
             if not self.wait_for_input:
-                self.modal(context, event)
+                mat_inv = context.object.matrix_world.inverted_safe()
+                point = mat_inv @ self.location
+                self.list_verts_co = make_line(self, self.geom, point)
 
             self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (), 'WINDOW', 'POST_VIEW')
 
diff --git a/mesh_snap_utilities_line/snap_context_l/__init__.py b/mesh_snap_utilities_line/snap_context_l/__init__.py
index 0cf5ad62c786dab8c7b7fab57051009abe7b30db..17e8ff545a754d4159ebd5c98dc2a2a55be231ca 100644
--- a/mesh_snap_utilities_line/snap_context_l/__init__.py
+++ b/mesh_snap_utilities_line/snap_context_l/__init__.py
@@ -28,6 +28,14 @@ FACE = 4
 
 
 class _Internal:
+    global_snap_context = None
+
+    @classmethod
+    def snap_context_free(cls):
+        if cls.global_snap_context != None:
+            cls.global_snap_context.free()
+            del cls
+
     from .mesh_drawing import (
         gpu_Indices_enable_state,
         gpu_Indices_restore_state,
@@ -56,8 +64,6 @@ class _SnapObjectData():
 class _SnapOffscreen():
     bound = None
     def __init__(self, width, height):
-        import ctypes
-
         self.freed = False
         self.is_bound = False
 
@@ -72,14 +78,9 @@ class _SnapOffscreen():
         self.cur_viewport = bgl.Buffer(bgl.GL_INT, 4)
 
         bgl.glGenRenderbuffers(1, self.buf_depth)
-        bgl.glBindRenderbuffer(bgl.GL_RENDERBUFFER, self.buf_depth[0])
-        bgl.glRenderbufferStorage(bgl.GL_RENDERBUFFER, bgl.GL_DEPTH_COMPONENT, width, height)
-
         bgl.glGenTextures(1, self.buf_color)
-        bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.buf_color[0])
-        NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0))
-        bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, width, height, 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL)
-        del NULL
+        self._config_textures()
+
         bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST)
         bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST)
 
@@ -87,7 +88,10 @@ class _SnapOffscreen():
 
         bgl.glGenFramebuffers(1, self.fbo)
         bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.fbo[0])
-        bgl.glFramebufferRenderbuffer(bgl.GL_FRAMEBUFFER, bgl.GL_DEPTH_ATTACHMENT, bgl.GL_RENDERBUFFER, self.buf_depth[0])
+        bgl.glFramebufferRenderbuffer(
+                bgl.GL_FRAMEBUFFER, bgl.GL_DEPTH_ATTACHMENT,
+                bgl.GL_RENDERBUFFER, self.buf_depth[0])
+
         bgl.glFramebufferTexture(bgl.GL_FRAMEBUFFER, bgl.GL_COLOR_ATTACHMENT0, self.buf_color[0], 0)
 
         bgl.glDrawBuffers(1, bgl.Buffer(bgl.GL_INT, 1, [bgl.GL_COLOR_ATTACHMENT0]))
@@ -98,6 +102,21 @@ class _SnapOffscreen():
 
         bgl.glBindFramebuffer(bgl.GL_FRAMEBUFFER, self.cur_fbo[0])
 
+    def _config_textures(self):
+        import ctypes
+
+        bgl.glBindRenderbuffer(bgl.GL_RENDERBUFFER, self.buf_depth[0])
+        bgl.glRenderbufferStorage(
+                bgl.GL_RENDERBUFFER, bgl.GL_DEPTH_COMPONENT, self.width, self.height)
+
+        NULL = bgl.Buffer(bgl.GL_INT, 1, (ctypes.c_int32 * 1).from_address(0))
+        bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.buf_color[0])
+        bgl.glTexImage2D(
+                bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, self.width, self.height,
+                0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL)
+        del NULL
+
+
     def bind(self):
         if self is not _SnapOffscreen.bound:
             if _SnapOffscreen.bound is None:
@@ -130,6 +149,19 @@ class _SnapOffscreen():
         if not is_bound:
             self.unbind()
 
+    def resize(self, width, height):
+        is_bound =  self is _SnapOffscreen.bound
+        if not is_bound:
+            self.bind()
+
+        self.width = int(width)
+        self.height = int(height)
+        self._config_textures()
+
+        if not is_bound:
+            self.unbind()
+
+
     def __del__(self):
         if not self.freed:
             bgl.glDeleteFramebuffers(1, self.fbo)
@@ -188,6 +220,7 @@ class SnapContext():
 
         self._offscreen.clear()
 
+
     ## PRIVATE ##
 
     def _get_snap_obj_by_index(self, index):
@@ -206,8 +239,8 @@ class SnapContext():
         d = 1
         m = self.threshold
         max_val = 2 * m - 1
-        last_value = -1
         find_next_index = self._snap_mode & FACE and self._snap_mode & (VERT | EDGE)
+        last_value = -1 if find_next_index else 0
         while m < max_val:
             for i in range(2):
                 while 2 * loc[i] * d < m:
@@ -218,7 +251,12 @@ class SnapContext():
                         if find_next_index:
                             last_value = value
                             r_snap_obj = self._get_snap_obj_by_index(value)
-                            if (r_snap_obj is None) or value < (r_snap_obj.data[1].first_index + len(r_snap_obj.data[1].tri_verts)):
+                            if r_snap_obj is not None:
+                                snap_data = r_snap_obj.data[1]
+                                if value < (snap_data.first_index + len(snap_data.tri_verts)):
+                                    # snap to a triangle
+                                    continue
+                            else:
                                 continue
                             find_next_index = False
                         elif (r_snap_obj is None) or\
@@ -290,6 +328,8 @@ class SnapContext():
             for snap_obj in self.snap_objects:
                 if len(snap_obj.data) == 2:
                     snap_obj.data[1].free()
+                    snap_obj.data.pop(1)
+
                 del snap_obj.data
                 del snap_obj.mat
                 del snap_obj
@@ -297,11 +337,37 @@ class SnapContext():
 
     ## PUBLIC ##
 
+    def update_viewport_context(self, region, space):
+        rv3d = space.region_3d
+
+        if region == self.region and rv3d == self.rv3d:
+            return
+
+        self.region = region
+        self.rv3d = rv3d
+
+        if self.rv3d.is_perspective:
+            self.depth_range = Vector((space.clip_start, space.clip_end))
+        else:
+            self.depth_range = Vector((-space.clip_end, space.clip_end))
+
+        self.winsize = Vector((self.region.width, self.region.height))
+        self._offscreen.resize(*self.winsize)
+
     def clear_snap_objects(self):
+        self.update_all()
         self.snap_objects.clear()
         _Internal.gpu_Indices_mesh_cache_clear()
 
     def update_all(self):
+        for snap_obj in self.snap_objects:
+            if len(snap_obj.data) == 2:
+                snap_obj.data[1].free()
+                snap_obj.data.pop(1)
+
+        self.update_drawing()
+
+    def update_drawing(self):
         self.drawn_count = 0
         self._offset_cur = 1
         self._offscreen.clear()
@@ -309,23 +375,27 @@ class SnapContext():
     def tag_update_drawn_snap_object(self, snap_obj):
         if len(snap_obj.data) > 1:
             snap_obj.data[1].free()
-            del snap_obj.data[1:]
-            #self.update_all()
+            snap_obj.data.pop(1)
+            #self.update_drawing()
             # Update on next snap_get call #
             self.proj_mat = None
 
     def update_drawn_snap_object(self, snap_obj):
-        if len(snap_obj.data) > 1:
-            _Internal.gpu_Indices_enable_state()
+        _Internal.gpu_Indices_enable_state()
 
-            from .mesh_drawing import GPU_Indices_Mesh
-            snap_vert = self._snap_mode & VERT != 0
-            snap_edge = self._snap_mode & EDGE != 0
-            snap_face = self._snap_mode & FACE != 0
+        from .mesh_drawing import GPU_Indices_Mesh
+        snap_vert = self._snap_mode & VERT != 0
+        snap_edge = self._snap_mode & EDGE != 0
+        snap_face = self._snap_mode & FACE != 0
+
+        if len(snap_obj.data) > 1:
             snap_obj.data[1].free()
-            snap_obj.data[1] = GPU_Indices_Mesh(snap_obj.data[0], snap_face, snap_edge, snap_vert)
+            snap_obj.data.pop(1)
+
+        data = GPU_Indices_Mesh(snap_obj.data[0], snap_face, snap_edge, snap_vert)
+        snap_obj.data.append(data)
 
-            _Internal.gpu_Indices_restore_state()
+        _Internal.gpu_Indices_restore_state()
 
     def use_clip_planes(self, value):
         _Internal.gpu_Indices_use_clip_planes(self.rv3d, value)
@@ -350,12 +420,7 @@ class SnapContext():
             self.update_all()
 
     def add_obj(self, obj, matrix):
-        matrix = matrix.copy()
-        snap_obj = self._get_snap_obj_by_obj(obj)
-        if not snap_obj:
-            self.snap_objects.append(_SnapObjectData([obj], matrix))
-        else:
-            self.snap_objects.append(_SnapObjectData(snap_obj.data, matrix))
+        self.snap_objects.append(_SnapObjectData([obj], matrix.copy()))
 
         return self.snap_objects[-1]
 
@@ -382,7 +447,7 @@ class SnapContext():
         if self.proj_mat != proj_mat:
             self.proj_mat = proj_mat
             _Internal.gpu_Indices_set_ProjectionMatrix(self.proj_mat)
-            self.update_all()
+            self.update_drawing()
 
         ray_dir, ray_orig = self.get_ray(mval)
         for i, snap_obj in enumerate(self.snap_objects[self.drawn_count:], self.drawn_count):
@@ -410,7 +475,12 @@ class SnapContext():
             if in_threshold:
                 if len(snap_obj.data) == 1:
                     from .mesh_drawing import GPU_Indices_Mesh
-                    snap_obj.data.append(GPU_Indices_Mesh(obj, snap_face, snap_edge, snap_vert))
+                    is_bound = obj.display_type == 'BOUNDS'
+                    draw_face = snap_face and not is_bound and obj.display_type != 'WIRE'
+                    draw_edge = snap_edge and not is_bound
+                    draw_vert = snap_vert and not is_bound
+                    snap_obj.data.append(GPU_Indices_Mesh(obj, draw_face, draw_edge, draw_vert))
+
                 snap_obj.data[1].set_draw_mode(snap_face, snap_edge, snap_vert)
                 snap_obj.data[1].set_ModelViewMatrix(snap_obj.mat)
 
@@ -449,3 +519,26 @@ class SnapContext():
     def free(self):
         self.__del__()
         self.freed = True
+
+def global_snap_context_get(region, space):
+    if _Internal.global_snap_context == None:
+        if region is None or space is None:
+            return
+
+        import atexit
+
+        _Internal.global_snap_context = SnapContext(region, space)
+
+        # Make sure we only registered the callback once.
+        atexit.unregister(_Internal.snap_context_free)
+        atexit.register(_Internal.snap_context_free)
+
+    elif region is not None:
+        _Internal.global_snap_context.update_viewport_context(region, space)
+
+        if ((region.width != _Internal.global_snap_context._offscreen.width) or
+            (region.height != _Internal.global_snap_context._offscreen.height)):
+                _Internal.global_snap_context.winsize[:] = region.width, region.height
+                _Internal.global_snap_context._offscreen.resize(region.width, region.height)
+
+    return _Internal.global_snap_context