# Global Metrics

path: .metrics.loc.ploc
old: 85.0
new: 152.0
path: .metrics.loc.cloc
old: 27.0
new: 18.0
path: .metrics.loc.lloc
old: 12.0
new: 48.0
path: .metrics.loc.sloc
old: 133.0
new: 195.0
path: .metrics.loc.blank
old: 21.0
new: 25.0
path: .metrics.nom.functions
old: 6.0
new: 9.0
path: .metrics.nom.total
old: 6.0
new: 9.0
path: .metrics.mi.mi_original
old: 51.93901821324039
new: 36.0724806458893
path: .metrics.mi.mi_sei
old: 32.07877219675028
new: 1.1535195012840838
path: .metrics.mi.mi_visual_studio
old: 30.373694861544088
new: 21.09501792157269
path: .metrics.cognitive.average
old: 0.0
new: 1.1111111111111112
path: .metrics.cognitive.sum
old: 0.0
new: 10.0
path: .metrics.cyclomatic.sum
old: 7.0
new: 21.0
path: .metrics.cyclomatic.average
old: 1.0
new: 1.75
path: .metrics.halstead.time
old: 1035.8891933524164
new: 9996.565240784055
path: .metrics.halstead.length
old: 256.0
new: 731.0
path: .metrics.halstead.volume
old: 1558.3904873600868
new: 5384.781538613692
path: .metrics.halstead.N2
old: 124.0
new: 327.0
path: .metrics.halstead.effort
old: 18646.005480343498
new: 179938.17433411302
path: .metrics.halstead.N1
old: 132.0
new: 404.0
path: .metrics.halstead.vocabulary
old: 68.0
new: 165.0
path: .metrics.halstead.purity_ratio
old: 1.4473768695796896
new: 1.5144135884859165
path: .metrics.halstead.difficulty
old: 11.964912280701755
new: 33.416058394160586
path: .metrics.halstead.n2
old: 57.0
new: 137.0
path: .metrics.halstead.n1
old: 11.0
new: 28.0
path: .metrics.halstead.level
old: 0.08357771260997067
new: 0.029925731760594144
path: .metrics.halstead.estimated_program_length
old: 370.52847861240053
new: 1107.036333183205
path: .metrics.halstead.bugs
old: 0.23438831142216415
new: 1.0624152234964572
path: .metrics.nexits.average
old: 1.0
new: 0.6666666666666666
path: .metrics.nargs.average
old: 2.3333333333333335
new: 1.2222222222222223
path: .metrics.nargs.sum
old: 14.0
new: 11.0

# Spaces Data

## Minimal test - lines (24, 195)

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.1111111111111112
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 10.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 72.95445336320968
new: 1055.9843510190187
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.58596637746108
new: 1.460559268352723
path: .spaces[0].metrics.halstead.length
old: 46.0
new: 723.0
path: .spaces[0].metrics.halstead.N1
old: 26.0
new: 404.0
path: .spaces[0].metrics.halstead.difficulty
old: 5.0
new: 34.091603053435115
path: .spaces[0].metrics.halstead.bugs
old: 0.03356034741635397
new: 1.0636407653375493
path: .spaces[0].metrics.halstead.n1
old: 7.0
new: 28.0
path: .spaces[0].metrics.halstead.effort
old: 1010.2330072391148
new: 180249.6137878691
path: .spaces[0].metrics.halstead.vocabulary
old: 21.0
new: 159.0
path: .spaces[0].metrics.halstead.n2
old: 14.0
new: 131.0
path: .spaces[0].metrics.halstead.volume
old: 202.04660144782295
new: 5287.214376670589
path: .spaces[0].metrics.halstead.time
old: 56.1240559577286
new: 10013.867432659395
path: .spaces[0].metrics.halstead.N2
old: 20.0
new: 319.0
path: .spaces[0].metrics.halstead.level
old: 0.2
new: 0.02933273622928795
path: .spaces[0].metrics.mi.mi_visual_studio
old: 61.005792427664645
new: 22.474120525419764
path: .spaces[0].metrics.mi.mi_sei
old: 74.90277755091917
new: 0.025243402639517853
path: .spaces[0].metrics.mi.mi_original
old: 104.31990505130652
new: 38.43074609846779
path: .spaces[0].metrics.nargs.sum
old: 3.0
new: 11.0
path: .spaces[0].metrics.nargs.average
old: 3.0
new: 1.2222222222222223
path: .spaces[0].metrics.nexits.sum
old: 1.0
new: 6.0
path: .spaces[0].metrics.nexits.average
old: 1.0
new: 0.6666666666666666
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.818181818181818
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 20.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 10.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 24.0
path: .spaces[0].metrics.loc.sloc
old: 11.0
new: 172.0
path: .spaces[0].metrics.loc.lloc
old: 2.0
new: 48.0
path: .spaces[0].metrics.loc.ploc
old: 11.0
new: 138.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 9.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 9.0

### Code

    namespace mozilla {
    namespace widget {

    static LazyLogModule sScreenLog("WidgetScreen");

    static void monitors_changed(GdkScreen* aScreen, gpointer aClosure) {
      MOZ_LOG(sScreenLog, LogLevel::Debug, ("Received monitors-changed event"));
      ScreenHelperGTK* self = static_cast(aClosure);
      self->RefreshScreens();
    }

    static void screen_resolution_changed(GdkScreen* aScreen, GParamSpec* aPspec,
                                          ScreenHelperGTK* self) {
      self->RefreshScreens();
    }

    static GdkFilterReturn root_window_event_filter(GdkXEvent* aGdkXEvent,
                                                    GdkEvent* aGdkEvent,
                                                    gpointer aClosure) {
    #ifdef MOZ_X11
      ScreenHelperGTK* self = static_cast(aClosure);
      XEvent* xevent = static_cast(aGdkXEvent);

      switch (xevent->type) {
        case PropertyNotify: {
          XPropertyEvent* propertyEvent = &xevent->xproperty;
          if (propertyEvent->atom == self->NetWorkareaAtom()) {
            MOZ_LOG(sScreenLog, LogLevel::Debug, ("Work area size changed"));
            self->RefreshScreens();
          }
        } break;
        default:
          break;
      }
    #endif

      return GDK_FILTER_CONTINUE;
    }

    ScreenHelperGTK::ScreenHelperGTK()
        : mRootWindow(nullptr)
    #ifdef MOZ_X11
          ,
          mNetWorkareaAtom(0)
    #endif
    {
      MOZ_LOG(sScreenLog, LogLevel::Debug, ("ScreenHelperGTK created"));
      GdkScreen* defaultScreen = gdk_screen_get_default();
      if (!defaultScreen) {
        // Sometimes we don't initial X (e.g., xpcshell)
        MOZ_LOG(sScreenLog, LogLevel::Debug,
                ("defaultScreen is nullptr, running headless"));
        return;
      }
      mRootWindow = gdk_get_default_root_window();
      MOZ_ASSERT(mRootWindow);

      g_object_ref(mRootWindow);

      // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
      gdk_window_set_events(mRootWindow,
                            GdkEventMask(gdk_window_get_events(mRootWindow) |
                                         GDK_PROPERTY_CHANGE_MASK));

      g_signal_connect(defaultScreen, "monitors-changed",
                       G_CALLBACK(monitors_changed), this);
      // Use _after to ensure this callback is run after gfxPlatformGtk.cpp's
      // handler.
      g_signal_connect_after(defaultScreen, "notify::resolution",
                             G_CALLBACK(screen_resolution_changed), this);
    #ifdef MOZ_X11
      gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
      if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
        mNetWorkareaAtom = XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow),
                                       "_NET_WORKAREA", X11False);
      }
    #endif
      RefreshScreens();
    }

    ScreenHelperGTK::~ScreenHelperGTK() {
      if (mRootWindow) {
        g_signal_handlers_disconnect_by_data(gdk_screen_get_default(), this);

        gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
        g_object_unref(mRootWindow);
        mRootWindow = nullptr;
      }
    }

    gint ScreenHelperGTK::GetGTKMonitorScaleFactor(gint aMonitorNum) {
      // Since GDK 3.10
      static auto sGdkScreenGetMonitorScaleFactorPtr =
          (gint(*)(GdkScreen*, gint))dlsym(RTLD_DEFAULT,
                                           "gdk_screen_get_monitor_scale_factor");
      if (sGdkScreenGetMonitorScaleFactorPtr) {
        GdkScreen* screen = gdk_screen_get_default();
        return sGdkScreenGetMonitorScaleFactorPtr(screen, aMonitorNum);
      }
      return 1;
    }

    static uint32_t GetGTKPixelDepth() {
      GdkVisual* visual = gdk_screen_get_system_visual(gdk_screen_get_default());
      return gdk_visual_get_depth(visual);
    }

    static already_AddRefed MakeScreen(GdkScreen* aScreen,
                                               gint aMonitorNum) {
      GdkRectangle monitor;
      GdkRectangle workarea;
      gdk_screen_get_monitor_geometry(aScreen, aMonitorNum, &monitor);
      gdk_screen_get_monitor_workarea(aScreen, aMonitorNum, &workarea);
      gint gdkScaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor(aMonitorNum);

      // gdk_screen_get_monitor_geometry / workarea returns application pixels
      // (desktop pixels), so we need to convert it to device pixels with
      // gdkScaleFactor.
      LayoutDeviceIntRect rect(
          monitor.x * gdkScaleFactor, monitor.y * gdkScaleFactor,
          monitor.width * gdkScaleFactor, monitor.height * gdkScaleFactor);
      LayoutDeviceIntRect availRect(
          workarea.x * gdkScaleFactor, workarea.y * gdkScaleFactor,
          workarea.width * gdkScaleFactor, workarea.height * gdkScaleFactor);
      uint32_t pixelDepth = GetGTKPixelDepth();

      // Use per-monitor scaling factor in gtk/wayland, or 1.0 otherwise.
      DesktopToLayoutDeviceScale contentsScale(1.0);
    #ifdef MOZ_WAYLAND
      GdkDisplay* gdkDisplay = gdk_display_get_default();
      if (!GDK_IS_X11_DISPLAY(gdkDisplay)) {
        contentsScale.scale = gdkScaleFactor;
      }
    #endif

      CSSToLayoutDeviceScale defaultCssScale(gdkScaleFactor *
                                             gfxPlatformGtk::GetFontScaleFactor());

      float dpi = 96.0f;
      gint heightMM = gdk_screen_get_monitor_height_mm(aScreen, aMonitorNum);
      if (heightMM > 0) {
        dpi = rect.height / (heightMM / MM_PER_INCH_FLOAT);
      }

      MOZ_LOG(sScreenLog, LogLevel::Debug,
              ("New screen [%d %d %d %d (%d %d %d %d) %d %f %f %f]", rect.x, rect.y,
               rect.width, rect.height, availRect.x, availRect.y, availRect.width,
               availRect.height, pixelDepth, contentsScale.scale,
               defaultCssScale.scale, dpi));
      RefPtr screen = new Screen(rect, availRect, pixelDepth, pixelDepth,
                                         contentsScale, defaultCssScale, dpi);
      return screen.forget();
    }

    void ScreenHelperGTK::RefreshScreens() {
      MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens"));
      AutoTArray, 4> screenList;

      GdkScreen* defaultScreen = gdk_screen_get_default();
      gint numScreens = gdk_screen_get_n_monitors(defaultScreen);
      MOZ_LOG(sScreenLog, LogLevel::Debug, ("GDK reports %d screens", numScreens));

      for (gint i = 0; i < numScreens; i++) {
        screenList.AppendElement(MakeScreen(defaultScreen, i));
      }

      ScreenManager& screenManager = ScreenManager::GetSingleton();
      screenManager.Refresh(std::move(screenList));
    }

    }  // namespace widget
    }  // namespace mozilla